ic_llm/lib.rs
1#![doc = include_str!("../README.md")]
2use candid::Principal;
3
4// Define our modules
5mod chat;
6mod tool;
7
8// Re-export public types from modules
9pub use chat::{AssistantMessage, ChatBuilder, ChatMessage, FunctionCall, Response, ToolCall};
10pub use tool::{
11 Function, ParameterBuilder, ParameterType, Parameters, Property, Tool, ToolBuilder,
12};
13
14// The mainnet principal of the LLM canister.
15const MAINNET_LLM_CANISTER: &str = "w36hm-eqaaa-aaaal-qr76a-cai";
16
17/// Resolves the LLM canister principal: prefers `PUBLIC_CANISTER_ID:llm` (auto-injected
18/// by `icp deploy`) and otherwise falls back to the mainnet canister.
19pub(crate) fn default_llm_canister() -> Principal {
20 // The env-var lookup only works in a canister.
21 // Skip in unit tests.
22 #[cfg(not(test))]
23 {
24 const LLM_CANISTER_ENV: &str = "PUBLIC_CANISTER_ID:llm";
25 if ic_cdk::api::env_var_name_exists(LLM_CANISTER_ENV) {
26 let id = ic_cdk::api::env_var_value(LLM_CANISTER_ENV);
27 return Principal::from_text(&id)
28 .unwrap_or_else(|e| ic_cdk::trap(format!("invalid {LLM_CANISTER_ENV}: {e}")));
29 }
30 }
31 Principal::from_text(MAINNET_LLM_CANISTER).unwrap()
32}
33
34/// Sends a single message to a model.
35///
36/// `model` is the canister's model identifier, e.g. `"llama3.1:8b"` (free) or
37/// `"gemma3:27b"` (paid). See the README for the current list.
38///
39/// # Example
40///
41/// ```
42/// # async fn prompt_example() -> String {
43/// ic_llm::prompt("llama3.1:8b", "What's the speed of light?").await
44/// # }
45/// ```
46pub async fn prompt<P: ToString>(model: impl Into<String>, prompt_str: P) -> String {
47 let response = ChatBuilder::new(model)
48 .with_messages(vec![ChatMessage::User {
49 content: prompt_str.to_string(),
50 }])
51 .send()
52 .await;
53
54 response.message.content.unwrap_or_default()
55}
56
57/// Creates a new ChatBuilder with the specified model.
58///
59/// This is a convenience function that returns a ChatBuilder instance initialized with the given model.
60/// You can then chain additional methods to configure the chat request before sending it.
61///
62/// # Example
63///
64/// ```
65/// use ic_llm::{ChatMessage, Response};
66///
67/// # async fn chat_example() -> Response {
68/// // Basic usage
69/// ic_llm::chat("llama3.1:8b")
70/// .with_messages(vec![
71/// ChatMessage::System {
72/// content: "You are a helpful assistant".to_string(),
73/// },
74/// ChatMessage::User {
75/// content: "How big is the sun?".to_string(),
76/// },
77/// ])
78/// .send()
79/// .await
80/// # }
81/// ```
82///
83/// You can also add tools to the chat:
84///
85/// ```
86/// use ic_llm::{ChatMessage, ParameterType, Response};
87///
88/// # async fn chat_with_tools_example() -> Response {
89/// ic_llm::chat("llama3.1:8b")
90/// .with_messages(vec![
91/// ChatMessage::System {
92/// content: "You are a helpful assistant".to_string(),
93/// },
94/// ChatMessage::User {
95/// content: "What's the balance of account abc123?".to_string(),
96/// },
97/// ])
98/// .with_tools(vec![
99/// ic_llm::tool("icp_account_balance")
100/// .with_description("Lookup the balance of an ICP account")
101/// .with_parameter(
102/// ic_llm::parameter("account", ParameterType::String)
103/// .with_description("The ICP account to look up")
104/// .is_required()
105/// )
106/// .build()
107/// ])
108/// .send()
109/// .await
110/// # }
111/// ```
112pub fn chat(model: impl Into<String>) -> ChatBuilder {
113 ChatBuilder::new(model)
114}
115
116/// Creates a new ToolBuilder with the specified name.
117///
118/// This is a convenience function that returns a ToolBuilder instance initialized with the given name.
119/// You can then chain additional methods to configure the tool before building it.
120///
121/// # Example
122///
123/// ```
124/// use ic_llm::{ParameterType, Response};
125///
126/// # fn tool_example() {
127/// // Basic usage
128/// let weather_tool = ic_llm::tool("get_weather")
129/// .with_description("Get current weather for a location")
130/// .with_parameter(
131/// ic_llm::parameter("location", ParameterType::String)
132/// .with_description("The location to get weather for")
133/// .is_required()
134/// )
135/// .build();
136/// # }
137/// ```
138pub fn tool<S: Into<String>>(name: S) -> ToolBuilder {
139 ToolBuilder::new(name)
140}
141
142/// Creates a new ParameterBuilder with the specified name and type.
143///
144/// This is a convenience function that returns a ParameterBuilder instance initialized with the given name and type.
145/// You can then chain additional methods to configure the parameter before adding it to a tool.
146///
147/// # Example
148///
149/// ```
150/// use ic_llm::ParameterType;
151///
152/// # fn parameter_example() {
153/// // Basic usage
154/// let location_param = ic_llm::parameter("location", ParameterType::String)
155/// .with_description("The location to get weather for")
156/// .is_required();
157/// # }
158/// ```
159pub fn parameter<S: Into<String>>(name: S, type_: ParameterType) -> ParameterBuilder {
160 ParameterBuilder::new(name, type_)
161}