mirror of
https://github.com/aiclientproxy/proxycast.git
synced 2026-09-24 23:10:56 +08:00
chore: prepare workspace migration and bump version to 0.47.3
This commit is contained in:
@@ -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
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "proxycast",
|
||||
"private": true,
|
||||
"version": "0.47.2",
|
||||
"version": "0.47.3",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
Generated
+77
-1
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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" }
|
||||
@@ -0,0 +1,7 @@
|
||||
//! Aster Agent 集成模块
|
||||
//!
|
||||
//! 包含 agent 相关功能,依赖 aster 框架
|
||||
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
@@ -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"] }
|
||||
@@ -0,0 +1,7 @@
|
||||
//! Tauri 应用入口模块
|
||||
//!
|
||||
//! 包含 app, commands, tray, services 等功能
|
||||
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
@@ -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"] }
|
||||
@@ -0,0 +1,7 @@
|
||||
//! 核心类型和工具模块
|
||||
//!
|
||||
//! 包含 models, config, database, logger 等基础功能
|
||||
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
@@ -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"] }
|
||||
@@ -0,0 +1,7 @@
|
||||
//! Provider 系统模块
|
||||
//!
|
||||
//! 包含 providers, credential, converter 等功能
|
||||
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
@@ -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"] }
|
||||
@@ -0,0 +1,7 @@
|
||||
//! API 服务器模块
|
||||
//!
|
||||
//! 包含 server, streaming, middleware, router 等功能
|
||||
|
||||
pub fn version() -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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 凭证信息
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user