chore: prepare workspace migration and bump version to 0.47.3

This commit is contained in:
coso
2026-01-16 00:52:36 +08:00
parent f7cf0aff4c
commit 99453292a9
20 changed files with 338 additions and 37 deletions
+25 -1
View File
@@ -4,6 +4,11 @@ on:
push:
tags:
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
- '.github/workflows/deploy-docs.yml'
- '.github/workflows/update-homebrew.yml'
workflow_dispatch:
inputs:
version:
@@ -17,7 +22,6 @@ permissions:
actions: read
env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
RUSTUP_MAX_RETRIES: 10
CARGO_TERM_COLOR: always
@@ -68,6 +72,9 @@ jobs:
with:
targets: ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
@@ -75,6 +82,7 @@ jobs:
shared-key: "rust-${{ matrix.target }}"
cache-on-failure: true
cache-all-crates: true
save-if: ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
- name: Install frontend dependencies
run: pnpm install --no-frozen-lockfile
@@ -109,6 +117,9 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_PROFILE_RELEASE_LTO: thin
CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 16
CARGO_INCREMENTAL: 0
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: sccache
# Apple 签名环境变量
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -144,6 +155,19 @@ jobs:
prerelease: false
args: --target ${{ matrix.target }}
# 构建后清理,节省缓存空间
- name: Post-build cleanup
run: |
rm -rf src-tauri/target/release/deps || true
rm -rf src-tauri/target/release/build || true
rm -rf src-tauri/target/release/incremental || true
rm -rf src-tauri/target/${{ matrix.target }}/release/deps || true
rm -rf src-tauri/target/${{ matrix.target }}/release/build || true
rm -rf src-tauri/target/${{ matrix.target }}/release/incremental || true
- name: Show sccache stats
run: sccache --show-stats
# 清理 keychain
- name: Cleanup Keychain
if: matrix.platform == 'macos-latest' && always()
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "proxycast",
"private": true,
"version": "0.47.2",
"version": "0.47.3",
"type": "module",
"repository": {
"type": "git",
+77 -1
View File
@@ -19,6 +19,21 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "agent"
version = "0.1.0"
dependencies = [
"anyhow",
"aster",
"core",
"futures",
"serde",
"serde_json",
"thiserror 1.0.69",
"tokio",
"tracing",
]
[[package]]
name = "ahash"
version = "0.8.12"
@@ -137,6 +152,22 @@ version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "app"
version = "0.1.0"
dependencies = [
"agent",
"anyhow",
"core",
"providers",
"serde",
"serde_json",
"server",
"thiserror 1.0.69",
"tokio",
"tracing",
]
[[package]]
name = "arboard"
version = "3.6.1"
@@ -1781,6 +1812,19 @@ dependencies = [
"url",
]
[[package]]
name = "core"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"serde",
"serde_json",
"thiserror 1.0.69",
"tracing",
"uuid",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -6057,9 +6101,24 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "providers"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"core",
"reqwest 0.12.28",
"serde",
"serde_json",
"thiserror 1.0.69",
"tokio",
"tracing",
]
[[package]]
name = "proxycast"
version = "0.47.2"
version = "0.47.3"
dependencies = [
"anyhow",
"arboard",
@@ -7302,6 +7361,23 @@ dependencies = [
"syn 2.0.114",
]
[[package]]
name = "server"
version = "0.1.0"
dependencies = [
"anyhow",
"axum 0.7.9",
"core",
"providers",
"serde",
"serde_json",
"thiserror 1.0.69",
"tokio",
"tower 0.4.13",
"tower-http 0.5.2",
"tracing",
]
[[package]]
name = "servo_arc"
version = "0.2.0"
+18 -7
View File
@@ -1,12 +1,23 @@
[package]
name = "proxycast"
version = "0.47.2"
description = "AI API Proxy Desktop App"
authors = ["you"]
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.package]
version = "0.47.3"
edition = "2021"
authors = ["you"]
repository = "https://github.com/aiclientproxy/proxycast"
homepage = "https://github.com/aiclientproxy/proxycast"
[package]
name = "proxycast"
version.workspace = true
description = "AI API Proxy Desktop App"
authors.workspace = true
edition.workspace = true
repository.workspace = true
homepage.workspace = true
[lib]
name = "proxycast_lib"
crate-type = ["lib", "cdylib", "staticlib"]
@@ -77,8 +88,8 @@ mouse_position = "0.1.4"
window-vibrancy = "0.7.1"
if-addrs = "0.13"
# Aster Agent Framework
aster = { git = "https://github.com/astercloud/aster-rust", branch = "main" }
# Aster Agent Framework - 使用固定 tag 避免每次重新拉取
aster = { git = "https://github.com/astercloud/aster-rust", tag = "v0.3.0" }
# Platform specific dependencies for browser interceptor
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "agent"
version = "0.1.0"
edition = "2021"
[dependencies]
core = { path = "../core" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"
thiserror = "1"
tracing = "0.1"
tokio = { version = "1", features = ["full"] }
futures = "0.3"
# Aster Agent Framework
aster = { git = "https://github.com/astercloud/aster-rust", tag = "v0.3.0" }
+7
View File
@@ -0,0 +1,7 @@
//! Aster Agent 集成模块
//!
//! 包含 agent 相关功能,依赖 aster 框架
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "app"
version = "0.1.0"
edition = "2021"
[dependencies]
core = { path = "../core" }
providers = { path = "../providers" }
server = { path = "../server" }
agent = { path = "../agent" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"
thiserror = "1"
tracing = "0.1"
tokio = { version = "1", features = ["full"] }
+7
View File
@@ -0,0 +1,7 @@
//! Tauri 应用入口模块
//!
//! 包含 app, commands, tray, services 等功能
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "core"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"
thiserror = "1"
tracing = "0.1"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1", features = ["v4"] }
+7
View File
@@ -0,0 +1,7 @@
//! 核心类型和工具模块
//!
//! 包含 models, config, database, logger 等基础功能
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "providers"
version = "0.1.0"
edition = "2021"
[dependencies]
core = { path = "../core" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"
thiserror = "1"
tracing = "0.1"
async-trait = "0.1"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.12", features = ["json", "stream"] }
+7
View File
@@ -0,0 +1,7 @@
//! Provider 系统模块
//!
//! 包含 providers, credential, converter 等功能
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "server"
version = "0.1.0"
edition = "2021"
[dependencies]
core = { path = "../core" }
providers = { path = "../providers" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
anyhow = "1"
thiserror = "1"
tracing = "0.1"
tokio = { version = "1", features = ["full"] }
axum = { version = "0.7", features = ["ws"] }
tower = "0.4"
tower-http = { version = "0.5", features = ["limit", "cors"] }
+7
View File
@@ -0,0 +1,7 @@
//! API 服务器模块
//!
//! 包含 server, streaming, middleware, router 等功能
pub fn version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
+2
View File
@@ -810,6 +810,8 @@ pub fn run() {
commands::api_key_provider_cmd::get_legacy_api_key_credentials,
commands::api_key_provider_cmd::migrate_legacy_api_key_credentials,
commands::api_key_provider_cmd::delete_legacy_api_key_credential,
// API Key Provider connection test command
commands::api_key_provider_cmd::test_api_key_provider_connection,
// Route commands
commands::route_cmd::get_available_routes,
commands::route_cmd::get_route_curl_examples,
@@ -9,7 +9,9 @@ use crate::database::dao::api_key_provider::{
ApiKeyEntry, ApiKeyProvider, ApiProviderType, ProviderWithKeys,
};
use crate::database::DbConnection;
use crate::services::api_key_provider_service::{ApiKeyProviderService, ConnectionTestResult, ImportResult};
use crate::services::api_key_provider_service::{
ApiKeyProviderService, ConnectionTestResult, ImportResult,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tauri::State;
@@ -605,5 +607,8 @@ pub async fn test_api_key_provider_connection(
provider_id: String,
model_name: Option<String>,
) -> Result<ConnectionTestResult, String> {
service.0.test_connection(&db, &provider_id, model_name).await
service
.0
.test_connection(&db, &provider_id, model_name)
.await
}
@@ -11,13 +11,32 @@ use crate::database::dao::api_key_provider::{
};
use crate::database::system_providers::{get_system_providers, to_api_key_provider};
use crate::database::DbConnection;
use crate::models::{CredentialData, CredentialSource, PoolProviderType, ProviderCredential};
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use chrono::Utc;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::RwLock;
// ============================================================================
// 连接测试结果
// ============================================================================
/// 连接测试结果
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConnectionTestResult {
/// 是否成功
pub success: bool,
/// 延迟(毫秒)
pub latency_ms: Option<u64>,
/// 错误信息
pub error: Option<String>,
/// 模型列表(如果使用 models 端点测试)
pub models: Option<Vec<String>>,
}
// ============================================================================
// 加密服务
// ============================================================================
@@ -1265,7 +1284,8 @@ impl ApiKeyProviderService {
) -> Result<Vec<String>, String> {
use crate::providers::openai_custom::OpenAICustomProvider;
let provider = OpenAICustomProvider::with_config(api_key.to_string(), Some(api_host.to_string()));
let provider =
OpenAICustomProvider::with_config(api_key.to_string(), Some(api_host.to_string()));
let response = provider
.list_models()
@@ -1273,7 +1293,7 @@ impl ApiKeyProviderService {
.map_err(|e| format!("获取模型列表失败: {}", e))?;
// 解析模型列表
let models = response["data"]
let models: Vec<String> = response["data"]
.as_array()
.map(|arr| {
arr.iter()
@@ -1296,23 +1316,28 @@ impl ApiKeyProviderService {
api_host: &str,
model: &str,
) -> Result<Vec<String>, String> {
use crate::models::openai::{ChatCompletionRequest, Message, MessageContent};
use crate::models::openai::{ChatCompletionRequest, ChatMessage, MessageContent};
use crate::providers::openai_custom::OpenAICustomProvider;
let provider = OpenAICustomProvider::with_config(api_key.to_string(), Some(api_host.to_string()));
let provider =
OpenAICustomProvider::with_config(api_key.to_string(), Some(api_host.to_string()));
let request = ChatCompletionRequest {
model: model.to_string(),
messages: vec![Message {
messages: vec![ChatMessage {
role: "user".to_string(),
content: Some(MessageContent::Text("hi".to_string())),
name: None,
tool_calls: None,
tool_call_id: None,
reasoning_content: None,
}],
temperature: None,
max_tokens: Some(1),
top_p: None,
stream: false,
..Default::default()
tools: None,
tool_choice: None,
reasoning_effort: None,
};
let response = provider
@@ -1338,7 +1363,8 @@ impl ApiKeyProviderService {
) -> Result<Vec<String>, String> {
use crate::providers::claude_custom::ClaudeCustomProvider;
let provider = ClaudeCustomProvider::with_config(api_key.to_string(), Some(api_host.to_string()));
let provider =
ClaudeCustomProvider::with_config(api_key.to_string(), Some(api_host.to_string()));
// 发送一个简单的测试请求
let request = serde_json::json!({
@@ -1397,7 +1423,7 @@ impl ApiKeyProviderService {
.await
.map_err(|e| format!("解析响应失败: {}", e))?;
let models = data["models"]
let models: Vec<String> = data["models"]
.as_array()
.map(|arr| {
arr.iter()
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "ProxyCast",
"version": "0.47.2",
"version": "0.47.3",
"identifier": "com.proxycast.app",
"build": {
"beforeDevCommand": "npm run dev",
@@ -130,9 +130,6 @@ export const ApiKeyProviderSection = forwardRef<
const handleTestConnection = useCallback(
async (providerId: string): Promise<ConnectionTestResult> => {
try {
// 调用后端测试连接 API
// 注意:这里需要后端实现 test_api_key_provider_connection 命令
// 暂时使用模拟实现
const provider = selectedProvider;
if (!provider || provider.api_keys.length === 0) {
return {
@@ -141,20 +138,23 @@ export const ApiKeyProviderSection = forwardRef<
};
}
// 尝试获取下一个 API Key 来验证连接
const apiKey = await apiKeyProviderApi.getNextApiKey(providerId);
if (!apiKey) {
return {
success: false,
error: "没有启用的 API Key",
};
}
// 如果 Provider 配置了自定义模型,使用第一个模型进行测试
const modelName =
provider.custom_models && provider.custom_models.length > 0
? provider.custom_models[0]
: undefined;
// TODO: 实现真正的连接测试
// 目前返回成功,后续需要调用后端的连接测试 API
// 调用后端连接测试 API
const result = await apiKeyProviderApi.testConnection(
providerId,
modelName,
);
// 转换后端返回的 latency_ms 为前端期望的 latencyMs
return {
success: true,
latencyMs: Math.floor(Math.random() * 200) + 50,
success: result.success,
latencyMs: result.latency_ms,
error: result.error,
};
} catch (e) {
return {
+44
View File
@@ -279,8 +279,52 @@ export const apiKeyProviderApi = {
async deleteLegacyCredential(uuid: string): Promise<boolean> {
return safeInvoke("delete_legacy_api_key_credential", { uuid });
},
// ============================================================================
// 连接测试 API
// ============================================================================
/**
* 测试 API Key Provider 连接
*
* 方案 C 实现:
* 1. 默认使用 /v1/models 端点测试
* 2. 如果提供了 modelName,用该模型发送简单请求
*
* @param providerId Provider ID
* @param modelName 可选的模型名称,用于发送测试请求
*/
async testConnection(
providerId: string,
modelName?: string,
): Promise<ConnectionTestResult> {
return safeInvoke("test_api_key_provider_connection", {
providerId,
modelName,
});
},
};
// ============================================================================
// 连接测试类型
// ============================================================================
/**
* 连接测试结果
*/
export interface ConnectionTestResult {
/** 是否成功 */
success: boolean;
/** 延迟(毫秒) */
latency_ms?: number;
/** 错误信息 */
error?: string;
/** 测试方法 */
method?: string;
/** 模型列表(如果使用 models 端点测试) */
models?: string[];
}
/**
* 旧的 API Key 凭证信息
*/