Skip to main content

asimov_cli/commands/proxy/
config.rs

1// This is free and unencumbered software released into the public domain.
2
3use crate::{BoxError, StandardOptions};
4use clap::ValueEnum;
5
6#[derive(Clone, Copy, Debug, ValueEnum)]
7pub enum ProxyConfigTarget {
8    /// Aider (https://aider.chat).
9    Aider,
10
11    /// Bash (https://gnu.org/software/bash/).
12    Bash,
13
14    /// Claude Code (https://claude.com/product/claude-code).
15    #[cfg(feature = "unstable")]
16    ClaudeCode,
17
18    /// Cline (https://cline.bot).
19    #[cfg(feature = "unstable")]
20    Cline,
21
22    /// .env (https://github.com/motdotla/dotenv).
23    Dotenv,
24
25    /// Goose (https://goose-docs.ai).
26    Goose,
27
28    /// LangChain (https://langchain.com).
29    Langchain,
30
31    /// LiteLLM (https://litellm.ai).
32    Litellm,
33
34    /// LlamaIndex (https://llamaindex.ai).
35    Llamaindex,
36
37    /// OpenCode (https://opencode.ai).
38    Opencode,
39
40    /// OpenHands, fka OpenDevin (https://openhands.dev).
41    Openhands,
42
43    /// Pi (https://pi.dev).
44    Pi,
45
46    /// PowerShell (https://microsoft.com/powershell).
47    Powershell,
48
49    /// Zed (https://zed.dev).
50    Zed,
51
52    /// Zsh (https://zsh.sourceforge.io).
53    Zsh,
54}
55
56pub async fn config(
57    app: ProxyConfigTarget,
58    format: Option<String>,
59    _flags: &StandardOptions,
60) -> Result<(), BoxError> {
61    use ProxyConfigTarget::*;
62    let base_url = "http://127.0.0.1:1920/v1"; // TODO
63    let default_model = "openrouter/free";
64    match app {
65        // See: <https://aider.chat/docs/llms/openai-compat.html>
66        Aider => match format.as_deref() {
67            None => {
68                let export = if cfg!(windows) { "setx" } else { "export" };
69                println!("{export} {}={}", "OPENAI_API_BASE", base_url);
70                println!("{export} {}={}", "OPENAI_API_KEY", "aider");
71            },
72            Some(_) => {},
73        },
74
75        Bash | Zsh => match format.as_deref() {
76            Some("export") | None => {
77                println!("export {}={}", "OPENAI_API_BASE", base_url);
78                println!("export {}={}", "OPENAI_API_KEY", "sh");
79            },
80            Some(_) => {},
81        },
82
83        #[cfg(feature = "unstable")]
84        ClaudeCode => todo!(), // TODO
85
86        // See: <https://docs.cline.bot/running-models-locally/overview>
87        #[cfg(feature = "unstable")]
88        Cline => todo!(), // TODO: no support for arbitrary endpoints?
89
90        Dotenv => match format.as_deref() {
91            Some("env") | None => {
92                println!("{}={}", "OPENAI_API_BASE", base_url);
93                println!("{}={}", "OPENAI_API_KEY", "dotenv");
94            },
95            Some(_) => {},
96        },
97
98        // See: <https://goose-docs.ai/docs/getting-started/providers/#configure-custom-provider>
99        // See: <https://github.com/aaif-goose/goose/blob/main/crates/goose-providers/src/declarative.rs#L112>
100        // See: <https://github.com/aaif-goose/goose/blob/main/crates/goose-providers/src/openai_compatible.rs>
101        Goose => match format.as_deref() {
102            Some("json") => {
103                print!("{}", include_str!("config/goose.json"));
104            },
105            Some("jsonc") | None => {
106                print!(
107                    "// {}\n{}",
108                    "~/.config/goose/custom_providers/asimov.json",
109                    include_str!("config/goose.json")
110                );
111            },
112            Some(_) => {},
113        },
114
115        // See: <https://reference.langchain.com/python/langchain-openai/chat_models/base/ChatOpenAI>
116        // See: <https://reference.langchain.com/javascript/langchain-openai/ChatOpenAI>
117        Langchain => match format.as_deref() {
118            Some("py") | None => {
119                print!("{}", include_str!("config/langchain.py"));
120            },
121            Some("js") | Some("ts") => {
122                print!("{}", include_str!("config/langchain.js"));
123            },
124            Some(_) => {},
125        },
126
127        // See: <https://docs.litellm.ai/docs/contributing/adding_openai_compatible_providers>
128        Litellm => match format.as_deref() {
129            Some("json") => {
130                print!("{}", include_str!("config/litellm.json"));
131            },
132            Some("jsonc") | None => {
133                print!(
134                    "// {}\n{}",
135                    "litellm/llms/openai_like/providers.json",
136                    include_str!("config/litellm.json")
137                );
138            },
139            Some(_) => {},
140        },
141
142        // See: <https://developers.llamaindex.ai/python/framework-api-reference/llms/openai_like/>
143        Llamaindex => match format.as_deref() {
144            Some("py") | None => {
145                print!("{}", include_str!("config/llamaindex.py"));
146            },
147            Some(_) => {},
148        },
149
150        // See: <https://opencode.ai/docs/providers/#custom-provider>
151        Opencode => match format.as_deref() {
152            Some("json") => {
153                print!("{}", include_str!("config/opencode.json"));
154            },
155            Some("jsonc") | None => {
156                print!(
157                    "// {}\n{}",
158                    "~/.config/opencode/opencode.json",
159                    include_str!("config/opencode.json")
160                );
161            },
162            Some(_) => {},
163        },
164
165        // See: <https://docs.openhands.dev/openhands/usage/llms/custom-llm-configs>
166        // See: <https://docs.openhands.dev/openhands/usage/v0/advanced/V0_configuration-options#llm-configuration>
167        Openhands => match format.as_deref() {
168            Some("env") => {
169                println!("{}={}", "LLM_BASE_URL", base_url);
170                println!("{}={}", "LLM_API_KEY", "openhands");
171                println!("{}={}", "LLM_MODEL", default_model);
172            },
173            Some("toml") | None => {
174                println!("[llm.asimov]");
175                println!("base_url = \"{}\"", base_url);
176                println!("api_key = \"{}\"", "openhands");
177                println!("model = \"{}\"", default_model);
178            },
179            Some(_) => {},
180        },
181
182        Powershell => match format.as_deref() {
183            Some("dotnet") | None => {
184                println!(
185                    r#"[Environment]::SetEnvironmentVariable("{}", "{}", "User")"#,
186                    "OPENAI_API_BASE", base_url
187                );
188                println!(
189                    r#"[Environment]::SetEnvironmentVariable("{}", "{}", "User")"#,
190                    "OPENAI_API_KEY", "powershell"
191                );
192            },
193            Some("set") | Some("setx") => {
194                println!("setx {}={}", "OPENAI_API_BASE", base_url);
195                println!("setx {}={}", "OPENAI_API_KEY", "powershell");
196            },
197            Some(_) => {},
198        },
199
200        // See: <https://pi.dev/docs/latest/providers#custom-providers>
201        // See: <https://pi.dev/docs/latest/models>
202        Pi => match format.as_deref() {
203            Some("json") => {
204                print!("{}", include_str!("config/pi.json"));
205            },
206            Some("jsonc") | None => {
207                print!(
208                    "// {}\n{}",
209                    "~/.pi/agent/models.json",
210                    include_str!("config/pi.json")
211                );
212            },
213            Some(_) => {},
214        },
215
216        // See: <https://zed.dev/docs/reference/all-settings#language-models>
217        Zed => match format.as_deref() {
218            Some("json") => {
219                println!("{}", zed_asimov_config()?);
220            },
221            Some("jsonc") | None => {
222                println!(
223                    "// {}\n{}",
224                    "~/.config/zed/settings.json",
225                    zed_asimov_config()?
226                );
227            },
228            Some(_) => {},
229        },
230    };
231    Ok(())
232}
233
234pub(crate) fn zed_asimov_config() -> Result<String, jsonc_parser::errors::ParseError> {
235    use jsonc_parser::{ParseOptions, cst::CstRootNode};
236    let cst = CstRootNode::parse(&"{}", &ParseOptions::default())?;
237    let root = cst.object_value_or_set();
238    root.object_value_or_set("language_models")
239        .object_value_or_set("openai_compatible")
240        .append("ASIMOV", zed_asimov_provider());
241    Ok(cst.to_string())
242}
243
244pub(crate) fn zed_asimov_provider() -> jsonc_parser::cst::CstInputValue {
245    use jsonc_parser::json;
246    json!({
247        "api_url": "http://127.0.0.1:1920/v1", // TODO: ASIMOV_PROXY_{BIND,PORT}
248        "available_models": [], // TODO: https://github.com/asimov-datasets/openrouter.ai
249    })
250}