zarz 0.3.1-alpha → 0.3.5-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/mcp/types.rs DELETED
@@ -1,170 +0,0 @@
1
- use serde::{Deserialize, Serialize};
2
- use serde_json::Value;
3
- use std::collections::HashMap;
4
-
5
- /// MCP JSON-RPC request
6
- #[derive(Debug, Clone, Serialize, Deserialize)]
7
- pub struct JsonRpcRequest {
8
- pub jsonrpc: String,
9
- pub id: u64,
10
- pub method: String,
11
- #[serde(skip_serializing_if = "Option::is_none")]
12
- pub params: Option<Value>,
13
- }
14
-
15
- /// MCP JSON-RPC response
16
- #[derive(Debug, Clone, Serialize, Deserialize)]
17
- pub struct JsonRpcResponse {
18
- pub jsonrpc: String,
19
- pub id: u64,
20
- #[serde(skip_serializing_if = "Option::is_none")]
21
- pub result: Option<Value>,
22
- #[serde(skip_serializing_if = "Option::is_none")]
23
- pub error: Option<JsonRpcError>,
24
- }
25
-
26
- /// MCP JSON-RPC error
27
- #[derive(Debug, Clone, Serialize, Deserialize)]
28
- pub struct JsonRpcError {
29
- pub code: i32,
30
- pub message: String,
31
- #[serde(skip_serializing_if = "Option::is_none")]
32
- pub data: Option<Value>,
33
- }
34
-
35
- /// MCP Tool definition
36
- #[derive(Debug, Clone, Serialize, Deserialize)]
37
- pub struct McpTool {
38
- pub name: String,
39
- pub description: Option<String>,
40
- #[serde(rename = "inputSchema")]
41
- pub input_schema: Value,
42
- }
43
-
44
- /// MCP Resource definition
45
- #[derive(Debug, Clone, Serialize, Deserialize)]
46
- #[allow(dead_code)]
47
- pub struct McpResource {
48
- pub uri: String,
49
- pub name: String,
50
- #[serde(skip_serializing_if = "Option::is_none")]
51
- pub description: Option<String>,
52
- #[serde(skip_serializing_if = "Option::is_none")]
53
- pub mime_type: Option<String>,
54
- }
55
-
56
- /// MCP Prompt definition
57
- #[derive(Debug, Clone, Serialize, Deserialize)]
58
- #[allow(dead_code)]
59
- pub struct McpPrompt {
60
- pub name: String,
61
- #[serde(skip_serializing_if = "Option::is_none")]
62
- pub description: Option<String>,
63
- #[serde(skip_serializing_if = "Option::is_none")]
64
- pub arguments: Option<Vec<PromptArgument>>,
65
- }
66
-
67
- #[derive(Debug, Clone, Serialize, Deserialize)]
68
- #[allow(dead_code)]
69
- pub struct PromptArgument {
70
- pub name: String,
71
- #[serde(skip_serializing_if = "Option::is_none")]
72
- pub description: Option<String>,
73
- pub required: bool,
74
- }
75
-
76
- /// MCP Initialize result
77
- #[derive(Debug, Clone, Serialize, Deserialize)]
78
- pub struct InitializeResult {
79
- #[serde(rename = "protocolVersion")]
80
- pub protocol_version: String,
81
- pub capabilities: ServerCapabilities,
82
- #[serde(rename = "serverInfo")]
83
- pub server_info: ServerInfo,
84
- }
85
-
86
- #[derive(Debug, Clone, Serialize, Deserialize)]
87
- pub struct ServerCapabilities {
88
- #[serde(skip_serializing_if = "Option::is_none")]
89
- pub tools: Option<ToolsCapability>,
90
- #[serde(skip_serializing_if = "Option::is_none")]
91
- pub resources: Option<ResourcesCapability>,
92
- #[serde(skip_serializing_if = "Option::is_none")]
93
- pub prompts: Option<PromptsCapability>,
94
- }
95
-
96
- #[derive(Debug, Clone, Serialize, Deserialize)]
97
- pub struct ToolsCapability {
98
- #[serde(skip_serializing_if = "Option::is_none")]
99
- pub list_changed: Option<bool>,
100
- }
101
-
102
- #[derive(Debug, Clone, Serialize, Deserialize)]
103
- pub struct ResourcesCapability {
104
- #[serde(skip_serializing_if = "Option::is_none")]
105
- pub subscribe: Option<bool>,
106
- #[serde(skip_serializing_if = "Option::is_none")]
107
- pub list_changed: Option<bool>,
108
- }
109
-
110
- #[derive(Debug, Clone, Serialize, Deserialize)]
111
- pub struct PromptsCapability {
112
- #[serde(skip_serializing_if = "Option::is_none")]
113
- pub list_changed: Option<bool>,
114
- }
115
-
116
- #[derive(Debug, Clone, Serialize, Deserialize)]
117
- pub struct ServerInfo {
118
- pub name: String,
119
- pub version: String,
120
- }
121
-
122
- /// Tools list response
123
- #[derive(Debug, Clone, Serialize, Deserialize)]
124
- pub struct ToolsListResult {
125
- pub tools: Vec<McpTool>,
126
- }
127
-
128
- /// Tool call request
129
- #[derive(Debug, Clone, Serialize, Deserialize)]
130
- #[allow(dead_code)]
131
- pub struct CallToolParams {
132
- pub name: String,
133
- #[serde(skip_serializing_if = "Option::is_none")]
134
- pub arguments: Option<HashMap<String, Value>>,
135
- }
136
-
137
- /// Tool call result
138
- #[derive(Debug, Clone, Serialize, Deserialize)]
139
- #[allow(dead_code)]
140
- pub struct CallToolResult {
141
- pub content: Vec<ToolContent>,
142
- #[serde(skip_serializing_if = "Option::is_none")]
143
- pub is_error: Option<bool>,
144
- }
145
-
146
- #[derive(Debug, Clone, Serialize, Deserialize)]
147
- #[serde(tag = "type")]
148
- #[allow(dead_code)]
149
- pub enum ToolContent {
150
- #[serde(rename = "text")]
151
- Text { text: String },
152
- #[serde(rename = "image")]
153
- Image { data: String, mime_type: String },
154
- #[serde(rename = "resource")]
155
- Resource { resource: McpResource },
156
- }
157
-
158
- /// Resources list response
159
- #[derive(Debug, Clone, Serialize, Deserialize)]
160
- #[allow(dead_code)]
161
- pub struct ResourcesListResult {
162
- pub resources: Vec<McpResource>,
163
- }
164
-
165
- /// Prompts list response
166
- #[derive(Debug, Clone, Serialize, Deserialize)]
167
- #[allow(dead_code)]
168
- pub struct PromptsListResult {
169
- pub prompts: Vec<McpPrompt>,
170
- }
@@ -1,214 +0,0 @@
1
- use anyhow::{anyhow, Context, Result};
2
- use bytes::Bytes;
3
- use futures::stream::StreamExt;
4
- use reqwest::Client;
5
- use serde::Deserialize;
6
- use serde_json::json;
7
-
8
- use super::{CompletionRequest, CompletionResponse, CompletionStream};
9
-
10
- const DEFAULT_ENDPOINT: &str = "https://api.anthropic.com/v1/messages";
11
- const DEFAULT_VERSION: &str = "2023-06-01";
12
-
13
- pub struct AnthropicClient {
14
- http: Client,
15
- endpoint: String,
16
- api_key: String,
17
- version: String,
18
- }
19
-
20
- impl AnthropicClient {
21
- pub fn from_env(
22
- api_key_override: Option<String>,
23
- endpoint_override: Option<String>,
24
- timeout_override: Option<u64>,
25
- ) -> Result<Self> {
26
- let api_key = api_key_override
27
- .or_else(|| std::env::var("ANTHROPIC_API_KEY").ok())
28
- .ok_or_else(|| anyhow::anyhow!("ANTHROPIC_API_KEY is required. Please set it in ~/.zarz/config.toml or as an environment variable"))?;
29
- let endpoint = endpoint_override
30
- .or_else(|| std::env::var("ANTHROPIC_API_URL").ok())
31
- .unwrap_or_else(|| DEFAULT_ENDPOINT.to_string());
32
- let version = std::env::var("ANTHROPIC_API_VERSION")
33
- .ok()
34
- .filter(|v| !v.trim().is_empty())
35
- .unwrap_or_else(|| DEFAULT_VERSION.to_string());
36
-
37
- let timeout_secs = timeout_override
38
- .or_else(|| {
39
- std::env::var("ANTHROPIC_TIMEOUT_SECS")
40
- .ok()
41
- .and_then(|raw| raw.parse::<u64>().ok())
42
- })
43
- .unwrap_or(120);
44
-
45
- let http = Client::builder()
46
- .user_agent("zarz-cli/0.1")
47
- .timeout(std::time::Duration::from_secs(timeout_secs))
48
- .build()
49
- .context("Failed to build HTTP client for Anthropic")?;
50
-
51
- Ok(Self {
52
- http,
53
- endpoint,
54
- api_key,
55
- version,
56
- })
57
- }
58
-
59
- pub async fn complete(&self, request: &CompletionRequest) -> Result<CompletionResponse> {
60
- let mut payload = serde_json::Map::new();
61
- payload.insert("model".to_string(), serde_json::Value::String(request.model.clone()));
62
- payload.insert(
63
- "max_tokens".to_string(),
64
- serde_json::Value::Number(serde_json::Number::from(request.max_output_tokens)),
65
- );
66
- payload.insert("temperature".to_string(), json!(request.temperature));
67
- if let Some(system_prompt) = &request.system_prompt {
68
- payload.insert(
69
- "system".to_string(),
70
- serde_json::Value::String(system_prompt.clone()),
71
- );
72
- }
73
- payload.insert(
74
- "messages".to_string(),
75
- json!([{
76
- "role": "user",
77
- "content": [{
78
- "type": "text",
79
- "text": request.user_prompt
80
- }]
81
- }]),
82
- );
83
-
84
- let response = self
85
- .http
86
- .post(&self.endpoint)
87
- .header("x-api-key", &self.api_key)
88
- .header("anthropic-version", &self.version)
89
- .json(&payload)
90
- .send()
91
- .await
92
- .context("Anthropic request failed")?;
93
-
94
- let response = response.error_for_status().context("Anthropic returned an error status")?;
95
- let parsed: AnthropicResponse = response
96
- .json()
97
- .await
98
- .context("Failed to decode Anthropic response")?;
99
- let text = parsed
100
- .content
101
- .into_iter()
102
- .find_map(|block| match block {
103
- AnthropicResponseBlock::Text { text, .. } => Some(text),
104
- })
105
- .ok_or_else(|| anyhow!("Anthropic response did not include text content"))?;
106
- Ok(CompletionResponse { text })
107
- }
108
-
109
- #[allow(dead_code)]
110
- pub async fn complete_stream(&self, request: &CompletionRequest) -> Result<CompletionStream> {
111
- let mut payload = serde_json::Map::new();
112
- payload.insert("model".to_string(), serde_json::Value::String(request.model.clone()));
113
- payload.insert(
114
- "max_tokens".to_string(),
115
- serde_json::Value::Number(serde_json::Number::from(request.max_output_tokens)),
116
- );
117
- payload.insert("temperature".to_string(), json!(request.temperature));
118
- payload.insert("stream".to_string(), json!(true));
119
-
120
- if let Some(system_prompt) = &request.system_prompt {
121
- payload.insert(
122
- "system".to_string(),
123
- serde_json::Value::String(system_prompt.clone()),
124
- );
125
- }
126
- payload.insert(
127
- "messages".to_string(),
128
- json!([{
129
- "role": "user",
130
- "content": [{
131
- "type": "text",
132
- "text": request.user_prompt
133
- }]
134
- }]),
135
- );
136
-
137
- let response = self
138
- .http
139
- .post(&self.endpoint)
140
- .header("x-api-key", &self.api_key)
141
- .header("anthropic-version", &self.version)
142
- .json(&payload)
143
- .send()
144
- .await
145
- .context("Anthropic streaming request failed")?;
146
-
147
- let response = response
148
- .error_for_status()
149
- .context("Anthropic returned an error status")?;
150
-
151
- let stream = response.bytes_stream();
152
- let text_stream = stream.map(|result| {
153
- let bytes = result?;
154
- parse_anthropic_sse_chunk(&bytes)
155
- });
156
-
157
- Ok(Box::pin(text_stream))
158
- }
159
- }
160
-
161
- #[allow(dead_code)]
162
- fn parse_anthropic_sse_chunk(bytes: &Bytes) -> Result<String> {
163
- let text = String::from_utf8_lossy(bytes);
164
- let mut result = String::new();
165
-
166
- for line in text.lines() {
167
- if let Some(data) = line.strip_prefix("data: ") {
168
- if data == "[DONE]" {
169
- break;
170
- }
171
-
172
- if let Ok(event) = serde_json::from_str::<StreamEvent>(data) {
173
- match event.event_type.as_str() {
174
- "content_block_delta" => {
175
- if let Some(delta) = event.delta {
176
- if let Some(text) = delta.text {
177
- result.push_str(&text);
178
- }
179
- }
180
- }
181
- _ => {}
182
- }
183
- }
184
- }
185
- }
186
-
187
- Ok(result)
188
- }
189
-
190
- #[allow(dead_code)]
191
- #[derive(Debug, Deserialize)]
192
- struct StreamEvent {
193
- #[serde(rename = "type")]
194
- event_type: String,
195
- delta: Option<StreamDelta>,
196
- }
197
-
198
- #[allow(dead_code)]
199
- #[derive(Debug, Deserialize)]
200
- struct StreamDelta {
201
- text: Option<String>,
202
- }
203
-
204
- #[derive(Debug, Deserialize)]
205
- struct AnthropicResponse {
206
- content: Vec<AnthropicResponseBlock>,
207
- }
208
-
209
- #[derive(Debug, Deserialize)]
210
- #[serde(tag = "type")]
211
- enum AnthropicResponseBlock {
212
- #[serde(rename = "text")]
213
- Text { text: String },
214
- }
@@ -1,209 +0,0 @@
1
- use anyhow::{anyhow, Context, Result};
2
- use bytes::Bytes;
3
- use futures::stream::StreamExt;
4
- use reqwest::Client;
5
- use serde::Deserialize;
6
- use serde_json::json;
7
-
8
- use super::{CompletionRequest, CompletionResponse, CompletionStream};
9
-
10
- // GLM Coding Plan endpoint (base URL only, no /chat/completions)
11
- const DEFAULT_ENDPOINT: &str = "https://api.z.ai/api/coding/paas/v4";
12
-
13
- pub struct GlmClient {
14
- http: Client,
15
- endpoint: String,
16
- api_key: String,
17
- }
18
-
19
- impl GlmClient {
20
- pub fn from_env(
21
- api_key_override: Option<String>,
22
- endpoint_override: Option<String>,
23
- timeout_override: Option<u64>,
24
- ) -> Result<Self> {
25
- let api_key = api_key_override
26
- .or_else(|| std::env::var("GLM_API_KEY").ok())
27
- .ok_or_else(|| anyhow::anyhow!("GLM_API_KEY is required. Please set it in ~/.zarz/config.toml or as an environment variable"))?;
28
- let endpoint = endpoint_override
29
- .or_else(|| std::env::var("GLM_API_URL").ok())
30
- .unwrap_or_else(|| DEFAULT_ENDPOINT.to_string());
31
-
32
- let timeout_secs = timeout_override
33
- .or_else(|| {
34
- std::env::var("GLM_TIMEOUT_SECS")
35
- .ok()
36
- .and_then(|raw| raw.parse::<u64>().ok())
37
- })
38
- .unwrap_or(120);
39
-
40
- let http = Client::builder()
41
- .user_agent("zarz-cli/0.1")
42
- .timeout(std::time::Duration::from_secs(timeout_secs))
43
- .build()
44
- .context("Failed to build HTTP client for GLM")?;
45
-
46
- Ok(Self {
47
- http,
48
- endpoint,
49
- api_key,
50
- })
51
- }
52
-
53
- pub async fn complete(&self, request: &CompletionRequest) -> Result<CompletionResponse> {
54
- let mut messages = Vec::new();
55
- if let Some(system) = &request.system_prompt {
56
- messages.push(json!({
57
- "role": "system",
58
- "content": system,
59
- }));
60
- }
61
- messages.push(json!({
62
- "role": "user",
63
- "content": request.user_prompt,
64
- }));
65
-
66
- let payload = json!({
67
- "model": request.model,
68
- "max_tokens": request.max_output_tokens,
69
- "messages": messages,
70
- });
71
-
72
- // Construct full endpoint URL
73
- let full_url = format!("{}/chat/completions", self.endpoint);
74
-
75
- let response = self
76
- .http
77
- .post(&full_url)
78
- .bearer_auth(&self.api_key)
79
- .json(&payload)
80
- .send()
81
- .await
82
- .context("GLM request failed")?;
83
-
84
- // Check status and get error details if failed
85
- let status = response.status();
86
- if !status.is_success() {
87
- let error_body = response.text().await.unwrap_or_else(|_| "Unable to read error body".to_string());
88
- return Err(anyhow!("GLM API error ({}): {}", status, error_body));
89
- }
90
-
91
- let response = response;
92
-
93
- let parsed: GlmResponse = response
94
- .json()
95
- .await
96
- .context("Failed to decode GLM response")?;
97
-
98
- let text = parsed
99
- .choices
100
- .into_iter()
101
- .find_map(|choice| choice.message.content)
102
- .ok_or_else(|| anyhow!("GLM response did not include content"))?;
103
-
104
- Ok(CompletionResponse { text })
105
- }
106
-
107
- #[allow(dead_code)]
108
- pub async fn complete_stream(&self, request: &CompletionRequest) -> Result<CompletionStream> {
109
- let mut messages = Vec::new();
110
- if let Some(system) = &request.system_prompt {
111
- messages.push(json!({
112
- "role": "system",
113
- "content": system,
114
- }));
115
- }
116
- messages.push(json!({
117
- "role": "user",
118
- "content": request.user_prompt,
119
- }));
120
-
121
- let payload = json!({
122
- "model": request.model,
123
- "max_tokens": request.max_output_tokens,
124
- "messages": messages,
125
- "stream": true,
126
- });
127
-
128
- // Construct full endpoint URL
129
- let full_url = format!("{}/chat/completions", self.endpoint);
130
-
131
- let response = self
132
- .http
133
- .post(&full_url)
134
- .bearer_auth(&self.api_key)
135
- .json(&payload)
136
- .send()
137
- .await
138
- .context("GLM streaming request failed")?;
139
-
140
- let response = response
141
- .error_for_status()
142
- .context("GLM returned an error status")?;
143
-
144
- let stream = response.bytes_stream();
145
- let text_stream = stream.map(|result| {
146
- let bytes = result?;
147
- parse_glm_sse_chunk(&bytes)
148
- });
149
-
150
- Ok(Box::pin(text_stream))
151
- }
152
- }
153
-
154
- #[allow(dead_code)]
155
- fn parse_glm_sse_chunk(bytes: &Bytes) -> Result<String> {
156
- let text = String::from_utf8_lossy(bytes);
157
- let mut result = String::new();
158
-
159
- for line in text.lines() {
160
- if let Some(data) = line.strip_prefix("data: ") {
161
- if data == "[DONE]" {
162
- break;
163
- }
164
-
165
- if let Ok(chunk) = serde_json::from_str::<StreamChunk>(data) {
166
- if let Some(choice) = chunk.choices.first() {
167
- if let Some(content) = &choice.delta.content {
168
- result.push_str(content);
169
- }
170
- }
171
- }
172
- }
173
- }
174
-
175
- Ok(result)
176
- }
177
-
178
- #[allow(dead_code)]
179
- #[derive(Debug, Deserialize)]
180
- struct StreamChunk {
181
- choices: Vec<StreamChoice>,
182
- }
183
-
184
- #[allow(dead_code)]
185
- #[derive(Debug, Deserialize)]
186
- struct StreamChoice {
187
- delta: StreamDelta,
188
- }
189
-
190
- #[allow(dead_code)]
191
- #[derive(Debug, Deserialize)]
192
- struct StreamDelta {
193
- content: Option<String>,
194
- }
195
-
196
- #[derive(Debug, Deserialize)]
197
- struct GlmResponse {
198
- choices: Vec<GlmChoice>,
199
- }
200
-
201
- #[derive(Debug, Deserialize)]
202
- struct GlmChoice {
203
- message: GlmMessage,
204
- }
205
-
206
- #[derive(Debug, Deserialize)]
207
- struct GlmMessage {
208
- content: Option<String>,
209
- }
@@ -1,90 +0,0 @@
1
- use anyhow::Result;
2
- use async_trait::async_trait;
3
- use futures::Stream;
4
- use std::pin::Pin;
5
-
6
- use crate::cli::Provider;
7
-
8
- mod anthropic;
9
- mod openai;
10
- mod glm;
11
-
12
- #[derive(Debug, Clone)]
13
- pub struct CompletionRequest {
14
- pub model: String,
15
- pub system_prompt: Option<String>,
16
- pub user_prompt: String,
17
- pub max_output_tokens: u32,
18
- pub temperature: f32,
19
- }
20
-
21
- #[derive(Debug, Clone)]
22
- pub struct CompletionResponse {
23
- pub text: String,
24
- }
25
-
26
- #[allow(dead_code)]
27
- pub type StreamChunk = Result<String>;
28
- #[allow(dead_code)]
29
- pub type CompletionStream = Pin<Box<dyn Stream<Item = StreamChunk> + Send>>;
30
-
31
- #[async_trait]
32
- pub trait CompletionProvider: Send + Sync {
33
- async fn complete(&self, request: &CompletionRequest) -> Result<CompletionResponse>;
34
- #[allow(dead_code)]
35
- async fn complete_stream(&self, request: &CompletionRequest) -> Result<CompletionStream>;
36
- }
37
-
38
- pub enum ProviderClient {
39
- Anthropic(anthropic::AnthropicClient),
40
- OpenAi(openai::OpenAiClient),
41
- Glm(glm::GlmClient),
42
- }
43
-
44
- impl ProviderClient {
45
- pub fn new(
46
- provider: Provider,
47
- api_key: Option<String>,
48
- endpoint_override: Option<String>,
49
- timeout_override: Option<u64>,
50
- ) -> Result<Self> {
51
- match provider {
52
- Provider::Anthropic => Ok(Self::Anthropic(
53
- anthropic::AnthropicClient::from_env(api_key, endpoint_override, timeout_override)?,
54
- )),
55
- Provider::OpenAi => Ok(Self::OpenAi(
56
- openai::OpenAiClient::from_env(api_key, endpoint_override, timeout_override)?,
57
- )),
58
- Provider::Glm => Ok(Self::Glm(
59
- glm::GlmClient::from_env(api_key, endpoint_override, timeout_override)?,
60
- )),
61
- }
62
- }
63
-
64
- pub fn name(&self) -> &'static str {
65
- match self {
66
- ProviderClient::Anthropic(_) => "anthropic",
67
- ProviderClient::OpenAi(_) => "openai",
68
- ProviderClient::Glm(_) => "glm",
69
- }
70
- }
71
- }
72
-
73
- #[async_trait]
74
- impl CompletionProvider for ProviderClient {
75
- async fn complete(&self, request: &CompletionRequest) -> Result<CompletionResponse> {
76
- match self {
77
- ProviderClient::Anthropic(client) => client.complete(request).await,
78
- ProviderClient::OpenAi(client) => client.complete(request).await,
79
- ProviderClient::Glm(client) => client.complete(request).await,
80
- }
81
- }
82
-
83
- async fn complete_stream(&self, request: &CompletionRequest) -> Result<CompletionStream> {
84
- match self {
85
- ProviderClient::Anthropic(client) => client.complete_stream(request).await,
86
- ProviderClient::OpenAi(client) => client.complete_stream(request).await,
87
- ProviderClient::Glm(client) => client.complete_stream(request).await,
88
- }
89
- }
90
- }