Skip to content

Commit d25ca66

Browse files
committed
fix: resolve all clippy warnings and add CI workflow
- Add Default trait implementations for all providers - Use inline format string variables (clippy::uninlined_format_args) - Use Range::contains for status code checks - Fix needless_range_loop in openai_to_cw converter - Run cargo fmt to fix formatting - Add GitHub Actions CI workflow with: - Rust clippy lint check - Rust format check - Frontend TypeScript check - Build verification
1 parent c9b4b78 commit d25ca66

18 files changed

Lines changed: 949 additions & 414 deletions

File tree

.github/workflows/ci.yml

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
lint:
14+
name: Lint & Format
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Setup Rust
22+
uses: dtolnay/rust-toolchain@stable
23+
with:
24+
components: clippy, rustfmt
25+
26+
- name: Setup Rust cache
27+
uses: Swatinem/rust-cache@v2
28+
with:
29+
workspaces: src-tauri
30+
shared-key: "rust-cache-lint"
31+
32+
- name: Install system dependencies (Linux)
33+
run: |
34+
sudo apt-get update
35+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev
36+
37+
- name: Check formatting
38+
working-directory: src-tauri
39+
run: cargo fmt --all -- --check
40+
41+
- name: Run Clippy
42+
working-directory: src-tauri
43+
run: cargo clippy --all-targets --all-features -- -D warnings
44+
45+
frontend-lint:
46+
name: Frontend Lint
47+
runs-on: ubuntu-latest
48+
49+
steps:
50+
- name: Checkout
51+
uses: actions/checkout@v4
52+
53+
- name: Setup Node.js
54+
uses: actions/setup-node@v4
55+
with:
56+
node-version: '20'
57+
cache: 'npm'
58+
59+
- name: Install dependencies
60+
run: npm ci
61+
62+
- name: TypeScript check
63+
run: npx tsc --noEmit
64+
65+
build-check:
66+
name: Build Check
67+
runs-on: ubuntu-latest
68+
needs: [lint, frontend-lint]
69+
70+
steps:
71+
- name: Checkout
72+
uses: actions/checkout@v4
73+
74+
- name: Setup Node.js
75+
uses: actions/setup-node@v4
76+
with:
77+
node-version: '20'
78+
cache: 'npm'
79+
80+
- name: Setup Rust
81+
uses: dtolnay/rust-toolchain@stable
82+
83+
- name: Setup Rust cache
84+
uses: Swatinem/rust-cache@v2
85+
with:
86+
workspaces: src-tauri
87+
shared-key: "rust-cache-build"
88+
89+
- name: Install system dependencies (Linux)
90+
run: |
91+
sudo apt-get update
92+
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev
93+
94+
- name: Install frontend dependencies
95+
run: npm ci
96+
97+
- name: Build frontend
98+
run: npm run build
99+
100+
- name: Check Rust build
101+
working-directory: src-tauri
102+
run: cargo check --all-targets
Lines changed: 60 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
//! Anthropic 格式转换为 OpenAI 格式 (支持 Claude Code)
2-
use crate::models::openai::*;
32
use crate::models::anthropic::*;
3+
use crate::models::openai::*;
44
use uuid::Uuid;
55

66
/// 将 Anthropic MessagesRequest 转换为 OpenAI ChatCompletionRequest
77
pub fn convert_anthropic_to_openai(request: &AnthropicMessagesRequest) -> ChatCompletionRequest {
88
let mut openai_messages: Vec<ChatMessage> = Vec::new();
9-
9+
1010
// 处理 system prompt
1111
if let Some(system) = &request.system {
1212
let system_text = extract_system_text(system);
@@ -19,27 +19,28 @@ pub fn convert_anthropic_to_openai(request: &AnthropicMessagesRequest) -> ChatCo
1919
});
2020
}
2121
}
22-
22+
2323
// 转换消息
2424
for msg in &request.messages {
2525
let converted = convert_anthropic_message(msg);
2626
openai_messages.extend(converted);
2727
}
28-
28+
2929
// 转换 tools
3030
let tools = request.tools.as_ref().map(|tools| {
31-
tools.iter().map(|t| {
32-
Tool {
31+
tools
32+
.iter()
33+
.map(|t| Tool {
3334
tool_type: "function".to_string(),
3435
function: FunctionDef {
3536
name: t.name.clone(),
3637
description: t.description.clone(),
3738
parameters: t.input_schema.clone(),
3839
},
39-
}
40-
}).collect()
40+
})
41+
.collect()
4142
});
42-
43+
4344
ChatCompletionRequest {
4445
model: request.model.clone(),
4546
messages: openai_messages,
@@ -54,25 +55,26 @@ pub fn convert_anthropic_to_openai(request: &AnthropicMessagesRequest) -> ChatCo
5455
fn extract_system_text(system: &serde_json::Value) -> String {
5556
match system {
5657
serde_json::Value::String(s) => s.clone(),
57-
serde_json::Value::Array(arr) => {
58-
arr.iter()
59-
.filter_map(|item| {
60-
if item.get("type") == Some(&serde_json::Value::String("text".to_string())) {
61-
item.get("text").and_then(|t| t.as_str()).map(|s| s.to_string())
62-
} else {
63-
None
64-
}
65-
})
66-
.collect::<Vec<_>>()
67-
.join("\n")
68-
}
58+
serde_json::Value::Array(arr) => arr
59+
.iter()
60+
.filter_map(|item| {
61+
if item.get("type") == Some(&serde_json::Value::String("text".to_string())) {
62+
item.get("text")
63+
.and_then(|t| t.as_str())
64+
.map(|s| s.to_string())
65+
} else {
66+
None
67+
}
68+
})
69+
.collect::<Vec<_>>()
70+
.join("\n"),
6971
_ => String::new(),
7072
}
7173
}
7274

7375
fn convert_anthropic_message(msg: &AnthropicMessage) -> Vec<ChatMessage> {
7476
let mut result: Vec<ChatMessage> = Vec::new();
75-
77+
7678
match &msg.content {
7779
serde_json::Value::String(s) => {
7880
result.push(ChatMessage {
@@ -86,10 +88,10 @@ fn convert_anthropic_message(msg: &AnthropicMessage) -> Vec<ChatMessage> {
8688
let mut text_parts: Vec<String> = Vec::new();
8789
let mut tool_calls: Vec<ToolCall> = Vec::new();
8890
let mut tool_results: Vec<(String, String)> = Vec::new(); // (tool_use_id, content)
89-
91+
9092
for part in parts {
9193
let part_type = part.get("type").and_then(|t| t.as_str()).unwrap_or("");
92-
94+
9395
match part_type {
9496
"text" => {
9597
if let Some(text) = part.get("text").and_then(|t| t.as_str()) {
@@ -98,11 +100,13 @@ fn convert_anthropic_message(msg: &AnthropicMessage) -> Vec<ChatMessage> {
98100
}
99101
"tool_use" => {
100102
let default_id = format!("call_{}", &Uuid::new_v4().to_string()[..8]);
101-
let id = part.get("id").and_then(|i| i.as_str())
103+
let id = part
104+
.get("id")
105+
.and_then(|i| i.as_str())
102106
.unwrap_or(&default_id);
103107
let name = part.get("name").and_then(|n| n.as_str()).unwrap_or("");
104108
let input = part.get("input").cloned().unwrap_or(serde_json::json!({}));
105-
109+
106110
tool_calls.push(ToolCall {
107111
id: id.to_string(),
108112
call_type: "function".to_string(),
@@ -113,21 +117,30 @@ fn convert_anthropic_message(msg: &AnthropicMessage) -> Vec<ChatMessage> {
113117
});
114118
}
115119
"tool_result" => {
116-
let tool_use_id = part.get("tool_use_id").and_then(|i| i.as_str()).unwrap_or("");
120+
let tool_use_id = part
121+
.get("tool_use_id")
122+
.and_then(|i| i.as_str())
123+
.unwrap_or("");
117124
let content = extract_tool_result_content(part.get("content"));
118125
tool_results.push((tool_use_id.to_string(), content));
119126
}
120127
_ => {}
121128
}
122129
}
123-
130+
124131
// 处理 assistant 消息
125132
if msg.role == "assistant" {
126-
let content = if text_parts.is_empty() { None } else {
127-
Some(MessageContent::Text(text_parts.join("")))
133+
let content = if text_parts.is_empty() {
134+
None
135+
} else {
136+
Some(MessageContent::Text(text_parts.join("")))
137+
};
138+
let tc = if tool_calls.is_empty() {
139+
None
140+
} else {
141+
Some(tool_calls)
128142
};
129-
let tc = if tool_calls.is_empty() { None } else { Some(tool_calls) };
130-
143+
131144
result.push(ChatMessage {
132145
role: "assistant".to_string(),
133146
content,
@@ -146,7 +159,7 @@ fn convert_anthropic_message(msg: &AnthropicMessage) -> Vec<ChatMessage> {
146159
tool_call_id: Some(tool_use_id),
147160
});
148161
}
149-
162+
150163
// 添加文本内容
151164
if !text_parts.is_empty() {
152165
result.push(ChatMessage {
@@ -160,25 +173,26 @@ fn convert_anthropic_message(msg: &AnthropicMessage) -> Vec<ChatMessage> {
160173
}
161174
_ => {}
162175
}
163-
176+
164177
result
165178
}
166179

167180
fn extract_tool_result_content(content: Option<&serde_json::Value>) -> String {
168181
match content {
169182
Some(serde_json::Value::String(s)) => s.clone(),
170-
Some(serde_json::Value::Array(arr)) => {
171-
arr.iter()
172-
.filter_map(|item| {
173-
if item.get("type") == Some(&serde_json::Value::String("text".to_string())) {
174-
item.get("text").and_then(|t| t.as_str()).map(|s| s.to_string())
175-
} else {
176-
None
177-
}
178-
})
179-
.collect::<Vec<_>>()
180-
.join("\n")
181-
}
183+
Some(serde_json::Value::Array(arr)) => arr
184+
.iter()
185+
.filter_map(|item| {
186+
if item.get("type") == Some(&serde_json::Value::String("text".to_string())) {
187+
item.get("text")
188+
.and_then(|t| t.as_str())
189+
.map(|s| s.to_string())
190+
} else {
191+
None
192+
}
193+
})
194+
.collect::<Vec<_>>()
195+
.join("\n"),
182196
_ => String::new(),
183197
}
184198
}

src-tauri/src/converter/cw_to_openai.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! CodeWhisperer 响应转换为 OpenAI 格式
2-
use crate::models::openai::*;
2+
#![allow(dead_code)]
3+
34
use crate::models::codewhisperer::*;
5+
use crate::models::openai::*;
46
use std::time::{SystemTime, UNIX_EPOCH};
57
use uuid::Uuid;
68

@@ -14,7 +16,7 @@ pub fn convert_cw_event_to_openai_chunk(
1416
.duration_since(UNIX_EPOCH)
1517
.unwrap()
1618
.as_secs();
17-
19+
1820
if let Some(resp_event) = &event.assistant_response_event {
1921
// 文本内容
2022
if let Some(content) = &resp_event.content {
@@ -34,7 +36,7 @@ pub fn convert_cw_event_to_openai_chunk(
3436
}],
3537
});
3638
}
37-
39+
3840
// Tool use
3941
if let Some(tool_use) = &resp_event.tool_use {
4042
return Some(ChatCompletionChunk {
@@ -52,7 +54,8 @@ pub fn convert_cw_event_to_openai_chunk(
5254
call_type: "function".to_string(),
5355
function: FunctionCall {
5456
name: tool_use.name.clone(),
55-
arguments: serde_json::to_string(&tool_use.input).unwrap_or_default(),
57+
arguments: serde_json::to_string(&tool_use.input)
58+
.unwrap_or_default(),
5659
},
5760
}]),
5861
},
@@ -61,7 +64,7 @@ pub fn convert_cw_event_to_openai_chunk(
6164
});
6265
}
6366
}
64-
67+
6568
None
6669
}
6770

@@ -77,9 +80,13 @@ pub fn create_openai_response(
7780
.duration_since(UNIX_EPOCH)
7881
.unwrap()
7982
.as_secs();
80-
81-
let finish_reason = if tool_calls.is_some() { "tool_calls" } else { "stop" };
82-
83+
84+
let finish_reason = if tool_calls.is_some() {
85+
"tool_calls"
86+
} else {
87+
"stop"
88+
};
89+
8390
ChatCompletionResponse {
8491
id: format!("chatcmpl-{}", Uuid::new_v4()),
8592
object: "chat.completion".to_string(),
@@ -89,7 +96,11 @@ pub fn create_openai_response(
8996
index: 0,
9097
message: ResponseMessage {
9198
role: "assistant".to_string(),
92-
content: if content.is_empty() { None } else { Some(content.to_string()) },
99+
content: if content.is_empty() {
100+
None
101+
} else {
102+
Some(content.to_string())
103+
},
93104
tool_calls,
94105
},
95106
finish_reason: finish_reason.to_string(),
@@ -108,7 +119,7 @@ pub fn create_stream_end_chunk(model: &str, response_id: &str) -> ChatCompletion
108119
.duration_since(UNIX_EPOCH)
109120
.unwrap()
110121
.as_secs();
111-
122+
112123
ChatCompletionChunk {
113124
id: response_id.to_string(),
114125
object: "chat.completion.chunk".to_string(),

0 commit comments

Comments
 (0)