diff options
| author | salmonik <salmonik@tuta.io> | 2026-07-13 17:21:21 +0200 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-07-13 17:21:21 +0200 |
| commit | 4961aae1737b77ff77102d37bf5f37f31a43824e (patch) | |
| tree | e344777946a6d4354312ffb638ece6e9cbbf3a0e /src/openrouter | |
| parent | make cron follow local time zone (diff) | |
| download | senator-sperma-4961aae1737b77ff77102d37bf5f37f31a43824e.tar.gz senator-sperma-4961aae1737b77ff77102d37bf5f37f31a43824e.zip | |
fix assistant and senator
Signed-off-by: salmonik <salmonik@tuta.io>
Diffstat (limited to 'src/openrouter')
| -rw-r--r-- | src/openrouter/mod.rs | 88 |
1 files changed, 41 insertions, 47 deletions
diff --git a/src/openrouter/mod.rs b/src/openrouter/mod.rs index 31cee85..1d3b443 100644 --- a/src/openrouter/mod.rs +++ b/src/openrouter/mod.rs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | use tracing::info; | ||
| 2 | |||
| 1 | use reqwest::Client; | 3 | use reqwest::Client; |
| 2 | use serde::{Deserialize, Serialize}; | 4 | use serde::{Deserialize, Serialize}; |
| 3 | 5 | ||
| @@ -7,6 +9,7 @@ use crate::lang; | |||
| 7 | pub struct OpenRouterClient { | 9 | pub struct OpenRouterClient { |
| 8 | api_key: String, | 10 | api_key: String, |
| 9 | model: String, | 11 | model: String, |
| 12 | api_url: String, | ||
| 10 | client: Client, | 13 | client: Client, |
| 11 | } | 14 | } |
| 12 | 15 | ||
| @@ -39,14 +42,49 @@ struct MessageContent { | |||
| 39 | } | 42 | } |
| 40 | 43 | ||
| 41 | impl OpenRouterClient { | 44 | impl OpenRouterClient { |
| 42 | pub fn new(api_key: String, model: String) -> Self { | 45 | /// `api_url` is the full URL including `/chat/completions` path. |
| 46 | pub fn new(api_url: String, api_key: String, model: String) -> Self { | ||
| 47 | info!("LLM client: url={api_url}, model={model}"); | ||
| 43 | Self { | 48 | Self { |
| 44 | api_key, | 49 | api_key, |
| 45 | model, | 50 | model, |
| 51 | api_url, | ||
| 46 | client: Client::new(), | 52 | client: Client::new(), |
| 47 | } | 53 | } |
| 48 | } | 54 | } |
| 49 | 55 | ||
| 56 | async fn do_request(&self, body: &ChatRequest) -> Result<String> { | ||
| 57 | let mut req = self | ||
| 58 | .client | ||
| 59 | .post(&self.api_url) | ||
| 60 | .header("Content-Type", "application/json") | ||
| 61 | .json(body); | ||
| 62 | |||
| 63 | if !self.api_key.is_empty() { | ||
| 64 | req = req.header("Authorization", format!("Bearer {}", self.api_key)); | ||
| 65 | } | ||
| 66 | |||
| 67 | let resp = req.send().await?; | ||
| 68 | |||
| 69 | if !resp.status().is_success() { | ||
| 70 | let status = resp.status(); | ||
| 71 | let text = resp.text().await.unwrap_or_default(); | ||
| 72 | return Err(crate::error::Error::Custom(lang::fmt_llm_err( | ||
| 73 | &self.api_url, | ||
| 74 | &status, | ||
| 75 | &text, | ||
| 76 | ))); | ||
| 77 | } | ||
| 78 | |||
| 79 | let chat_resp: ChatResponse = resp.json().await?; | ||
| 80 | Ok(chat_resp | ||
| 81 | .choices | ||
| 82 | .into_iter() | ||
| 83 | .next() | ||
| 84 | .map(|c| c.message.content) | ||
| 85 | .unwrap_or_default()) | ||
| 86 | } | ||
| 87 | |||
| 50 | pub async fn chat(&self, system_prompt: &str, user_message: &str) -> Result<String> { | 88 | pub async fn chat(&self, system_prompt: &str, user_message: &str) -> Result<String> { |
| 51 | let body = ChatRequest { | 89 | let body = ChatRequest { |
| 52 | model: self.model.clone(), | 90 | model: self.model.clone(), |
| @@ -63,29 +101,7 @@ impl OpenRouterClient { | |||
| 63 | max_tokens: 500, | 101 | max_tokens: 500, |
| 64 | }; | 102 | }; |
| 65 | 103 | ||
| 66 | let resp = self | 104 | self.do_request(&body).await |
| 67 | .client | ||
| 68 | .post("https://openrouter.ai/api/v1/chat/completions") | ||
| 69 | .header("Authorization", format!("Bearer {}", self.api_key)) | ||
| 70 | .json(&body) | ||
| 71 | .send() | ||
| 72 | .await?; | ||
| 73 | |||
| 74 | if !resp.status().is_success() { | ||
| 75 | let status = resp.status(); | ||
| 76 | let text = resp.text().await.unwrap_or_default(); | ||
| 77 | return Err(crate::error::Error::Custom(lang::fmt_openrouter_err( | ||
| 78 | &status, &text, | ||
| 79 | ))); | ||
| 80 | } | ||
| 81 | |||
| 82 | let chat_resp: ChatResponse = resp.json().await?; | ||
| 83 | Ok(chat_resp | ||
| 84 | .choices | ||
| 85 | .into_iter() | ||
| 86 | .next() | ||
| 87 | .map(|c| c.message.content) | ||
| 88 | .unwrap_or_default()) | ||
| 89 | } | 105 | } |
| 90 | 106 | ||
| 91 | pub async fn chat_with_history( | 107 | pub async fn chat_with_history( |
| @@ -107,28 +123,6 @@ impl OpenRouterClient { | |||
| 107 | max_tokens: 500, | 123 | max_tokens: 500, |
| 108 | }; | 124 | }; |
| 109 | 125 | ||
| 110 | let resp = self | 126 | self.do_request(&body).await |
| 111 | .client | ||
| 112 | .post("https://openrouter.ai/api/v1/chat/completions") | ||
| 113 | .header("Authorization", format!("Bearer {}", self.api_key)) | ||
| 114 | .json(&body) | ||
| 115 | .send() | ||
| 116 | .await?; | ||
| 117 | |||
| 118 | if !resp.status().is_success() { | ||
| 119 | let status = resp.status(); | ||
| 120 | let text = resp.text().await.unwrap_or_default(); | ||
| 121 | return Err(crate::error::Error::Custom(lang::fmt_openrouter_err( | ||
| 122 | &status, &text, | ||
| 123 | ))); | ||
| 124 | } | ||
| 125 | |||
| 126 | let chat_resp: ChatResponse = resp.json().await?; | ||
| 127 | Ok(chat_resp | ||
| 128 | .choices | ||
| 129 | .into_iter() | ||
| 130 | .next() | ||
| 131 | .map(|c| c.message.content) | ||
| 132 | .unwrap_or_default()) | ||
| 133 | } | 127 | } |
| 134 | } | 128 | } |
