main
rs 123 lines 4.89 KB
Raw
1 //! Tauri bindings for [Ed](ed_agent::Ed).
2 //!
3 //! Ed's core is framework-agnostic and reports through a
4 //! [`StatusSink`](ed_agent::StatusSink). This crate is the Tauri implementation
5 //! of that sink plus the commands a webview calls.
6 //!
7 //! # Wiring it up
8 //!
9 //! Build an [`Ed`](ed_agent::Ed) around a [`TauriSink`] and manage it during
10 //! setup:
11 //!
12 //! ```no_run
13 //! use ed_agent::Ed;
14 //! use ed_agent_tauri::{EdState, TauriSink};
15 //! use tauri::Manager;
16 //!
17 //! fn setup(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
18 //! let ed = Ed::with_sink(TauriSink::new(app.handle().clone()));
19 //! app.manage(EdState::new(ed));
20 //! Ok(())
21 //! }
22 //! ```
23 //!
24 //! Then register the commands on the builder. This can't be a doc test — a
25 //! library has no `tauri.conf.json` for `generate_context!` to read:
26 //!
27 //! ```text
28 //! tauri::Builder::default()
29 //! .setup(|app| setup(app))
30 //! .invoke_handler(tauri::generate_handler![
31 //! ed_agent_tauri::chat_get_status,
32 //! ed_agent_tauri::chat_get_history,
33 //! ed_agent_tauri::chat_clear_history,
34 //! ed_agent_tauri::chat_send_message,
35 //! ed_agent_tauri::chat_run,
36 //! ed_agent_tauri::chat_cancel,
37 //! ])
38 //! .run(tauri::generate_context!())
39 //! .expect("run");
40 //! ```
41 //!
42 //! Loading a model is deliberately not a command here: which model, and from
43 //! where, is an application decision. Call [`Ed::load`](ed_agent::Ed::load)
44 //! from your own command or during setup.
45 //!
46 //! # Several conversations
47 //!
48 //! For an app with a session list, build an [`EdSessions`] instead of an
49 //! [`EdState`]. You supply a [`SessionStore`](ed_agent::SessionStore); it owns
50 //! the live session, the cache, the logout fence and approvals:
51 //!
52 //! ```text
53 //! let sessions = EdSessions::builder(app.handle().clone(), MyStore::new())
54 //! .executor(my_executor)
55 //! .build();
56 //! app.manage(sessions);
57 //! ```
58 //!
59 //! Register `chat_list_sessions`, `chat_new_session`, `chat_switch_session`,
60 //! `chat_delete_session`, `chat_get_session_history`, `chat_submit` and
61 //! `chat_respond_approval`. `chat_submit` returns at once and the answer comes
62 //! as a `chat_reply` event `{id, session, reply, duration, error}`, with
63 //! `chat_text_delta {session, id, delta}` events while it generates.
64 //!
65 //! A host that has to rewrite the message first (to add context) or recover
66 //! from an engine failure wraps [`EdSessions::submit_with`] in a command of its
67 //! own. Call [`EdSessions::reset`] on logout or account switch.
68 //!
69 //! # Tools
70 //!
71 //! Tool calling works through this crate, but the host supplies the executor.
72 //! With [`EdSessions`] approvals are handled for you: [`TauriApprovals`] emits
73 //! `chat_approval_requested {request_id, session, call, risk}`, waits (120 s by
74 //! default) for `chat_respond_approval {request_id, decision}`, then emits
75 //! `chat_approval_resolved {request_id}`. A timeout or a reset denies.
76 //!
77 //! With [`EdState`], build the agent with
78 //! [`Ed::with_agent`](ed_agent::Ed::with_agent) and your own
79 //! [`ApprovalHandler`](ed_agent::ApprovalHandler); the default `Ed::with_sink`
80 //! rejects every tool and denies every approval.
81 //!
82 //! # Events
83 //!
84 //! The first two are named exactly as the existing applications already emit
85 //! them, so a frontend needs no change:
86 //!
87 //! - [`EVENT_CHAT_STATUS_CHANGED`] with a [`ChatStatusPayload`]
88 //! - [`EVENT_CHAT_REPLY`] with a [`ChatReply`](ed_agent::ChatReply)
89 //! - [`EVENT_CHAT_TOOL_REQUESTED`] with a [`ToolCall`](ed_agent::ToolCall)
90 //! - [`EVENT_CHAT_APPROVAL_REQUESTED`] with an
91 //! [`ApprovalRequest`](ed_agent::ApprovalRequest)
92 //! - [`EVENT_CHAT_TOOL_STARTED`] with a [`ToolCall`](ed_agent::ToolCall)
93 //! - [`EVENT_CHAT_TOOL_FINISHED`] with a
94 //! [`ToolExecutionResult`](ed_agent::ToolExecutionResult)
95 //! - [`EVENT_CHAT_AGENT_REPLY`] with an [`AgentReply`](ed_agent::AgentReply)
96 //! - [`EVENT_CHAT_WARNING`] with a string
97 //! - [`EVENT_CHAT_TEXT_DELTA`] with a [`TextDeltaPayload`]
98 //! - [`EVENT_CHAT_APPROVAL_RESOLVED`] with an [`ApprovalResolvedPayload`]
99
100 #![forbid(unsafe_code)]
101
102 mod approvals;
103 mod commands;
104 mod events;
105 mod scope;
106 mod sessions;
107 mod sink;
108
109 pub use approvals::{TauriApprovals, DEFAULT_APPROVAL_TIMEOUT};
110 pub use commands::{
111 chat_cancel, chat_clear_history, chat_delete_session, chat_get_history,
112 chat_get_session_history, chat_get_status, chat_list_sessions, chat_new_session,
113 chat_respond_approval, chat_run, chat_send_message, chat_submit, chat_switch_session, EdState,
114 };
115 pub use events::{
116 ApprovalRequestedPayload, ApprovalResolvedPayload, ChatStatusPayload, SubmitReplyPayload,
117 TextDeltaPayload, EVENT_CHAT_AGENT_REPLY, EVENT_CHAT_APPROVAL_REQUESTED,
118 EVENT_CHAT_APPROVAL_RESOLVED, EVENT_CHAT_REPLY, EVENT_CHAT_STATUS_CHANGED,
119 EVENT_CHAT_TEXT_DELTA, EVENT_CHAT_TOOL_FINISHED, EVENT_CHAT_TOOL_REQUESTED,
120 EVENT_CHAT_TOOL_STARTED, EVENT_CHAT_WARNING,
121 };
122 pub use sessions::{EdSessions, EdSessionsBuilder};
123 pub use sink::TauriSink;