Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/btcw_dsp-deepseekpartner.json
T

1 line
63 KiB
JSON

{"repo": "btcw/dsp-deepseekpartner", "files": {"src/types.ts": {"b35a2ef3e22ab74127c50dfef56ddd52b503b418": {"t": 1785746037.66955, "n": 2406, "h": "a2fe57cfc2ad0245", "c": "export type ApiSurface = \"anthropic\" | \"openAi\";\nexport type ServiceStatusKind = \"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"error\";\nexport type LogLevel = \"error\" | \"info\" | \"debug\";\n\nexport interface FeatureFlags {\n normalizeAdaptiveThinking: boolean;\n mapEffort: boolean;\n preserveSse: boolean;\n smoothStreamingText: boolean;\n reasoningReplay: boolean;\n oneMContextDefaults: boolean;\n redactSensitiveLogs: boolean;\n}\n\nexport interface ModelMapping {\n main: string;\n opus: string;\n sonnet: string;\n haiku: string;\n subagent: string;\n}\n\nexport interface GatewayProfile {\n id: string;\n name: string;\n port: number;\n upstreamBaseUrl: string;\n apiKey?: string | null;\n enabledSurfaces: ApiSurface[];\n modelMapping: ModelMapping;\n timeoutSeconds: number;\n logLevel: LogLevel;\n features: FeatureFlags;\n}\n\nexport interface ProfileStatus {\n id: string;\n status: ServiceStatusKind;\n port: number;\n proxyOrigin: string;\n lastError?: string | null;\n startedAt?: string | null;\n requestCount: number;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: string;\n profileId: string;\n requestId?: string | null;\n message: string;\n}\n\nexport interface SkillConfig {\n id: string;\n name: string;\n description: string;\n instructions: string;\n enabled: boolean;\n}\n\nexport interface AppSettings {\n mcpConfig: unknown;\n skills: SkillConfig[];\n}\n\nexport const defaultProfile = (port = 17777): GatewayProfile => ({\n id: \"\",\n name: \"DeepSeek Local\",\n port,\n upstreamBaseUrl: \"https://api.deepseek.com\",\n apiKey: \"\",\n enabledSurfaces: [\"anthropic\", \"openAi\"],\n modelMapping: {\n main: \"deepseek-v4-pro[1m]\",\n opus: \"deepseek-v4-pro[1m]\",\n sonnet: \"deepseek-v4-pro[1m]\",\n haiku: \"deepseek-v4-flash\",\n subagent: \"deepseek-v4-flash\"\n },\n timeoutSeconds: 120,\n logLevel: \"info\",\n features: {\n normalizeAdaptiveThinking: true,\n mapEffort: true,\n preserveSse: true,\n smoothStreamingText: true,\n reasoningReplay: true,\n oneMContextDefaults: true,\n redactSensitiveLogs: true\n }\n});\n\nexport const defaultSettings = (): AppSettings => ({\n mcpConfig: defaultMcpConfig(),\n skills: []\n});\n\nexport const defaultMcpConfig = () => ({\n mcpServers: {\n \"network-request\": {\n type: \"builtin\",\n enabled: true,\n tool: \"network_request\",\n description: \"HTTP/HTTPS request helper executed by DSP-deepseekPartner\"\n }\n }\n});\n"}, "f970669c12c078db99697080a3530174c3cde33d": {"t": 1785759095.8763318, "n": 2406, "h": "a2fe57cfc2ad0245", "c": "export type ApiSurface = \"anthropic\" | \"openAi\";\nexport type ServiceStatusKind = \"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"error\";\nexport type LogLevel = \"error\" | \"info\" | \"debug\";\n\nexport interface FeatureFlags {\n normalizeAdaptiveThinking: boolean;\n mapEffort: boolean;\n preserveSse: boolean;\n smoothStreamingText: boolean;\n reasoningReplay: boolean;\n oneMContextDefaults: boolean;\n redactSensitiveLogs: boolean;\n}\n\nexport interface ModelMapping {\n main: string;\n opus: string;\n sonnet: string;\n haiku: string;\n subagent: string;\n}\n\nexport interface GatewayProfile {\n id: string;\n name: string;\n port: number;\n upstreamBaseUrl: string;\n apiKey?: string | null;\n enabledSurfaces: ApiSurface[];\n modelMapping: ModelMapping;\n timeoutSeconds: number;\n logLevel: LogLevel;\n features: FeatureFlags;\n}\n\nexport interface ProfileStatus {\n id: string;\n status: ServiceStatusKind;\n port: number;\n proxyOrigin: string;\n lastError?: string | null;\n startedAt?: string | null;\n requestCount: number;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: string;\n profileId: string;\n requestId?: string | null;\n message: string;\n}\n\nexport interface SkillConfig {\n id: string;\n name: string;\n description: string;\n instructions: string;\n enabled: boolean;\n}\n\nexport interface AppSettings {\n mcpConfig: unknown;\n skills: SkillConfig[];\n}\n\nexport const defaultProfile = (port = 17777): GatewayProfile => ({\n id: \"\",\n name: \"DeepSeek Local\",\n port,\n upstreamBaseUrl: \"https://api.deepseek.com\",\n apiKey: \"\",\n enabledSurfaces: [\"anthropic\", \"openAi\"],\n modelMapping: {\n main: \"deepseek-v4-pro[1m]\",\n opus: \"deepseek-v4-pro[1m]\",\n sonnet: \"deepseek-v4-pro[1m]\",\n haiku: \"deepseek-v4-flash\",\n subagent: \"deepseek-v4-flash\"\n },\n timeoutSeconds: 120,\n logLevel: \"info\",\n features: {\n normalizeAdaptiveThinking: true,\n mapEffort: true,\n preserveSse: true,\n smoothStreamingText: true,\n reasoningReplay: true,\n oneMContextDefaults: true,\n redactSensitiveLogs: true\n }\n});\n\nexport const defaultSettings = (): AppSettings => ({\n mcpConfig: defaultMcpConfig(),\n skills: []\n});\n\nexport const defaultMcpConfig = () => ({\n mcpServers: {\n \"network-request\": {\n type: \"builtin\",\n enabled: true,\n tool: \"network_request\",\n description: \"HTTP/HTTPS request helper executed by DSP-deepseekPartner\"\n }\n }\n});\n"}, "86c5dfb6573583d77ab2f8e55f8ed6545963fefe": {"t": 1785759097.0387497, "n": 2316, "h": "bcfa45d0d5d56226", "c": "export type ApiSurface = \"anthropic\" | \"openAi\";\nexport type ServiceStatusKind = \"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"error\";\nexport type LogLevel = \"error\" | \"info\" | \"debug\";\n\nexport interface FeatureFlags {\n normalizeAdaptiveThinking: boolean;\n mapEffort: boolean;\n preserveSse: boolean;\n smoothStreamingText: boolean;\n reasoningReplay: boolean;\n oneMContextDefaults: boolean;\n redactSensitiveLogs: boolean;\n}\n\nexport interface ModelMapping {\n main: string;\n opus: string;\n sonnet: string;\n haiku: string;\n subagent: string;\n}\n\nexport interface GatewayProfile {\n id: string;\n name: string;\n port: number;\n upstreamBaseUrl: string;\n apiKey?: string | null;\n enabledSurfaces: ApiSurface[];\n modelMapping: ModelMapping;\n timeoutSeconds: number;\n logLevel: LogLevel;\n features: FeatureFlags;\n}\n\nexport interface ProfileStatus {\n id: string;\n status: ServiceStatusKind;\n port: number;\n proxyOrigin: string;\n lastError?: string | null;\n startedAt?: string | null;\n requestCount: number;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: string;\n profileId: string;\n requestId?: string | null;\n message: string;\n}\n\nexport interface McpServiceConfig {\n id: string;\n name: string;\n command: string;\n args: string;\n env: string;\n description: string;\n enabled: boolean;\n}\n\nexport interface SkillConfig {\n id: string;\n name: string;\n description: string;\n instructions: string;\n enabled: boolean;\n}\n\nexport interface AppSettings {\n mcpServices: McpServiceConfig[];\n skills: SkillConfig[];\n}\n\nexport const defaultProfile = (port = 17777): GatewayProfile => ({\n id: \"\",\n name: \"DeepSeek Local\",\n port,\n upstreamBaseUrl: \"https://api.deepseek.com\",\n apiKey: \"\",\n enabledSurfaces: [\"anthropic\", \"openAi\"],\n modelMapping: {\n main: \"deepseek-v4-pro[1m]\",\n opus: \"deepseek-v4-pro[1m]\",\n sonnet: \"deepseek-v4-pro[1m]\",\n haiku: \"deepseek-v4-flash\",\n subagent: \"deepseek-v4-flash\"\n },\n timeoutSeconds: 120,\n logLevel: \"info\",\n features: {\n normalizeAdaptiveThinking: true,\n mapEffort: true,\n preserveSse: true,\n smoothStreamingText: true,\n reasoningReplay: true,\n oneMContextDefaults: true,\n redactSensitiveLogs: true\n }\n});\n\nexport const defaultSettings = (): AppSettings => ({\n mcpServices: [],\n skills: []\n});\n"}, "477e32f76c148692457d5bb9bf5fefde7bb24cc9": {"t": 1785759098.249932, "n": 1799, "h": "dc584b335edb63d1", "c": "export type ApiSurface = \"anthropic\" | \"openAi\";\nexport type ServiceStatusKind = \"stopped\" | \"starting\" | \"running\" | \"stopping\" | \"error\";\nexport type LogLevel = \"error\" | \"info\" | \"debug\";\n\nexport interface FeatureFlags {\n normalizeAdaptiveThinking: boolean;\n mapEffort: boolean;\n preserveSse: boolean;\n smoothStreamingText: boolean;\n reasoningReplay: boolean;\n oneMContextDefaults: boolean;\n redactSensitiveLogs: boolean;\n}\n\nexport interface ModelMapping {\n main: string;\n opus: string;\n sonnet: string;\n haiku: string;\n subagent: string;\n}\n\nexport interface GatewayProfile {\n id: string;\n name: string;\n port: number;\n upstreamBaseUrl: string;\n enabledSurfaces: ApiSurface[];\n modelMapping: ModelMapping;\n timeoutSeconds: number;\n logLevel: LogLevel;\n features: FeatureFlags;\n}\n\nexport interface ProfileStatus {\n id: string;\n status: ServiceStatusKind;\n port: number;\n proxyOrigin: string;\n lastError?: string | null;\n startedAt?: string | null;\n requestCount: number;\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: string;\n profileId: string;\n requestId?: string | null;\n message: string;\n}\n\nexport const defaultProfile = (port = 17777): GatewayProfile => ({\n id: \"\",\n name: \"DeepSeek Local\",\n port,\n upstreamBaseUrl: \"https://api.deepseek.com\",\n enabledSurfaces: [\"anthropic\", \"openAi\"],\n modelMapping: {\n main: \"deepseek-v4-pro[1m]\",\n opus: \"deepseek-v4-pro[1m]\",\n sonnet: \"deepseek-v4-pro[1m]\",\n haiku: \"deepseek-v4-flash\",\n subagent: \"deepseek-v4-flash\"\n },\n timeoutSeconds: 120,\n logLevel: \"info\",\n features: {\n normalizeAdaptiveThinking: true,\n mapEffort: true,\n preserveSse: true,\n smoothStreamingText: true,\n reasoningReplay: true,\n oneMContextDefaults: true,\n redactSensitiveLogs: true\n }\n});\n"}}, "src-tauri/src/models.rs": {"b35a2ef3e22ab74127c50dfef56ddd52b503b418": {"t": 1785746254.2045994, "n": 15933, "h": "0f855d60159e42ab", "c": "use serde::{Deserialize, Serialize};\nuse serde_json::{json, Map, Value};\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ApiSurface {\n Anthropic,\n OpenAi,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ServiceStatusKind {\n Stopped,\n Starting,\n Running,\n Stopping,\n Error,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum LogLevel {\n Error,\n Info,\n Debug,\n}\n\nimpl Default for LogLevel {\n fn default() -> Self {\n Self::Info\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct FeatureFlags {\n pub normalize_adaptive_thinking: bool,\n pub map_effort: bool,\n pub preserve_sse: bool,\n #[serde(default = \"default_true\")]\n pub smooth_streaming_text: bool,\n pub reasoning_replay: bool,\n pub one_m_context_defaults: bool,\n pub redact_sensitive_logs: bool,\n}\n\nfn default_true() -> bool {\n true\n}\n\nimpl Default for FeatureFlags {\n fn default() -> Self {\n Self {\n normalize_adaptive_thinking: true,\n map_effort: true,\n preserve_sse: true,\n smooth_streaming_text: true,\n reasoning_replay: true,\n one_m_context_defaults: true,\n redact_sensitive_logs: true,\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ModelMapping {\n pub main: String,\n pub opus: String,\n pub sonnet: String,\n pub haiku: String,\n pub subagent: String,\n}\n\nimpl Default for ModelMapping {\n fn default() -> Self {\n Self {\n main: \"deepseek-v4-pro[1m]\".into(),\n opus: \"deepseek-v4-pro[1m]\".into(),\n sonnet: \"deepseek-v4-pro[1m]\".into(),\n haiku: \"deepseek-v4-flash\".into(),\n subagent: \"deepseek-v4-flash\".into(),\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct GatewayProfile {\n pub id: String,\n pub name: String,\n pub port: u16,\n pub upstream_base_url: String,\n #[serde(default, skip_serializing_if = \"Option::is_none\")]\n pub api_key: Option<String>,\n pub enabled_surfaces: Vec<ApiSurface>,\n pub model_mapping: ModelMapping,\n pub timeout_seconds: u64,\n pub log_level: LogLevel,\n pub features: FeatureFlags,\n}\n\nimpl GatewayProfile {\n pub fn new_default(id: String, name: String, port: u16) -> Self {\n Self {\n id,\n name,\n port,\n upstream_base_url: \"https://api.deepseek.com\".into(),\n api_key: None,\n enabled_surfaces: vec![ApiSurface::Anthropic, ApiSurface::OpenAi],\n model_mapping: ModelMapping::default(),\n timeout_seconds: 120,\n log_level: LogLevel::default(),\n features: FeatureFlags::default(),\n }\n }\n\n pub fn validate(&self) -> Result<(), String> {\n let trimmed_name = self.name.trim();\n if trimmed_name.is_empty() {\n return Err(\"Profile name is required\".into());\n }\n if self.port < 1024 {\n return Err(\"Port must be 1024 or higher\".into());\n }\n if self.enabled_surfaces.is_empty() {\n return Err(\"At least one API surface must be enabled\".into());\n }\n if !(self.upstream_base_url.starts_with(\"https://\")\n || self.upstream_base_url.starts_with(\"http://\"))\n {\n return Err(\"Upstream base URL must start with http:// or https://\".into());\n }\n if self.timeout_seconds == 0 || self.timeout_seconds > 600 {\n return Err(\"Timeout must be between 1 and 600 seconds\".into());\n }\n Ok(())\n }\n\n pub fn normalized(mut self) -> Self {\n self.name = self.name.trim().to_string();\n self.upstream_base_url = self\n .upstream_base_url\n .trim()\n .trim_end_matches('/')\n .to_string();\n self.api_key = self.api_key.and_then(|key| {\n let trimmed = key.trim().to_string();\n if trimmed.is_empty() {\n None\n } else {\n Some(trimmed)\n }\n });\n self\n }\n\n pub fn fallback_api_key(&self) -> Option<&str> {\n self.api_key\n .as_deref()\n .map(str::trim)\n .filter(|key| !key.is_empty())\n }\n\n pub fn proxy_origin(&self) -> String {\n format!(\"http://127.0.0.1:{}\", self.port)\n }\n\n pub fn anthropic_base_url(&self) -> String {\n format!(\"{}/anthropic\", self.proxy_origin())\n }\n\n pub fn openai_base_url(&self) -> String {\n self.proxy_origin()\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct AppSettings {\n #[serde(default = \"default_mcp_config\")]\n pub mcp_config: Value,\n #[serde(default)]\n #[serde(skip_serializing_if = \"Vec::is_empty\")]\n pub mcp_services: Vec<McpServiceConfig>,\n #[serde(default)]\n pub skills: Vec<SkillConfig>,\n}\n\nimpl Default for AppSettings {\n fn default() -> Self {\n Self {\n mcp_config: default_mcp_config(),\n mcp_services: Vec::new(),\n skills: Vec::new(),\n }\n }\n}\n\nimpl AppSettings {\n pub fn normalized(mut self) -> Self {\n let legacy_services = self\n .mcp_services\n .into_iter()\n .map(McpServiceConfig::normalized)\n .filter(|item| !item.name.is_empty() || !item.command.is_empty())\n .collect::<Vec<_>>();\n self.mcp_config = normalize_mcp_config(self.mcp_config, &legacy_services);\n self.mcp_services = Vec::new();\n self.skills = self\n .skills\n .into_iter()\n .map(SkillConfig::normalized)\n .filter(|item| !item.name.is_empty() || !item.instructions.is_empty())\n .collect();\n self\n }\n\n pub fn validate(&self) -> Result<(), String> {\n if !self.mcp_config.is_object() {\n return Err(\"MCP config must be a JSON object\".into());\n }\n for skill in &self.skills {\n if skill.enabled && skill.name.trim().is_empty() {\n return Err(\"Enabled skills must have a name\".into());\n }\n if skill.enabled && skill.instructions.trim().is_empty() {\n return Err(\"Enabled skills must have instructions\".into());\n }\n }\n Ok(())\n }\n\n pub fn network_request_mcp_enabled(&self) -> bool {\n mcp_server_enabled(&self.mcp_config, \"network-request\")\n || mcp_server_enabled(&self.mcp_config, \"network_request\")\n || self\n .mcp_config\n .pointer(\"/builtin/networkRequest/enabled\")\n .and_then(Value::as_bool)\n .unwrap_or(false)\n }\n\n pub fn request_context(&self) -> Option<String> {\n let services = mcp_server_summaries(&self.mcp_config);\n let skills = self\n .skills\n .iter()\n .filter(|item| item.enabled)\n .collect::<Vec<_>>();\n if services.is_empty() && skills.is_empty() {\n return None;\n }\n\n let mut lines = Vec::from([\n \"DSP-deepseekPartner configured context.\".to_string(),\n \"Use these entries as user-configured guidance for DeepSeek-compatible clients.\"\n .to_string(),\n ]);\n if !skills.is_empty() {\n lines.push(\"Skills:\".to_string());\n for skill in skills {\n let description = if skill.description.is_empty() {\n String::new()\n } else {\n format!(\" - {}\", skill.description)\n };\n lines.push(format!(\"- {}{description}\", skill.name));\n lines.push(format!(\" Instructions: {}\", skill.instructions));\n }\n }\n if !services.is_empty() {\n lines.push(\"MCP services:\".to_string());\n lines.push(\n \"Enabled MCP definitions are available to DSP-deepseekPartner where supported.\"\n .to_string(),\n );\n for service in &services {\n lines.push(format!(\"- {service}\"));\n }\n }\n Some(lines.join(\"\\n\"))\n }\n}\n\nfn default_mcp_config() -> Value {\n json!({\n \"mcpServers\": {\n \"network-request\": {\n \"type\": \"builtin\",\n \"enabled\": true,\n \"tool\": \"network_request\",\n \"description\": \"HTTP/HTTPS request helper executed by DSP-deepseekPartner\"\n }\n }\n })\n}\n\nfn normalize_mcp_config(config: Value, legacy_services: &[McpServiceConfig]) -> Value {\n let mut config = if config.is_null() {\n default_mcp_config()\n } else {\n config\n };\n if !config.is_object() {\n return config;\n }\n if !legacy_services.is_empty() {\n let root = config.as_object_mut().expect(\"object checked above\");\n let servers = root\n .entry(\"mcpServers\")\n .or_insert_with(|| Value::Object(Map::new()));\n if let Some(servers) = servers.as_object_mut() {\n for service in legacy_services {\n servers.insert(\n service.name.clone(),\n json!({\n \"command\": service.command,\n \"args\": split_legacy_args(&service.args),\n \"env\": parse_legacy_env(&service.env),\n \"description\": service.description,\n \"enabled\": service.enabled\n }),\n );\n }\n }\n }\n config\n}\n\nfn split_legacy_args(args: &str) -> Vec<String> {\n args.split_whitespace().map(str::to_string).collect()\n}\n\nfn parse_legacy_env(env: &str) -> Map<String, Value> {\n env.lines()\n .filter_map(|line| line.split_once('='))\n .map(|(key, value)| {\n (\n key.trim().to_string(),\n Value::String(value.trim().to_string()),\n )\n })\n .collect()\n}\n\nfn mcp_server_enabled(config: &Value, name: &str) -> bool {\n config\n .pointer(&format!(\"/mcpServers/{name}/enabled\"))\n .and_then(Value::as_bool)\n .unwrap_or(false)\n}\n\nfn mcp_server_summaries(config: &Value) -> Vec<String> {\n let Some(servers) = config.get(\"mcpServers\").and_then(Value::as_object) else {\n return Vec::new();\n };\n servers\n .iter()\n .filter(|(_, value)| {\n value\n .get(\"enabled\")\n .and_then(Value::as_bool)\n .unwrap_or(true)\n })\n .map(|(name, value)| {\n let description = value\n .get(\"description\")\n .and_then(Value::as_str)\n .filter(|item| !item.trim().is_empty())\n .map(|item| format!(\" - {}\", item.trim()))\n .unwrap_or_default();\n let command = value\n .get(\"command\")\n .and_then(Value::as_str)\n .filter(|item| !item.trim().is_empty())\n .or_else(|| value.get(\"type\").and_then(Value::as_str))\n .unwrap_or(\"configured\");\n format!(\"{name}: {command}{description}\")\n })\n .collect()\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct McpServiceConfig {\n pub id: String,\n pub name: String,\n pub command: String,\n #[serde(default)]\n pub args: String,\n #[serde(default)]\n pub env: String,\n #[serde(default)]\n pub description: String,\n #[serde(default = \"default_true\")]\n pub enabled: bool,\n}\n\nimpl McpServiceConfig {\n fn normalized(mut self) -> Self {\n self.id = self.id.trim().to_string();\n self.name = self.name.trim().to_string();\n self.command = self.command.trim().to_string();\n self.args = self.args.trim().to_string();\n self.env = self.env.trim().to_string();\n self.description = self.description.trim().to_string();\n self\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct SkillConfig {\n pub id: String,\n pub name: String,\n #[serde(default)]\n pub description: String,\n pub instructions: String,\n #[serde(default = \"default_true\")]\n pub enabled: bool,\n}\n\nimpl SkillConfig {\n fn normalized(mut self) -> Self {\n self.id = self.id.trim().to_string();\n self.name = self.name.trim().to_string();\n self.description = self.description.trim().to_string();\n self.instructions = self.instructions.trim().to_string();\n self\n }\n}\n\npub fn canonical_model_id(model: &str) -> String {\n match model {\n \"deepseek-v4-pro[1m]\" => \"deepseek-v4-pro\".to_string(),\n \"deepseek-v4-flash[1m]\" => \"deepseek-v4-flash\".to_string(),\n other => other.to_string(),\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProfileStatus {\n pub id: String,\n pub status: ServiceStatusKind,\n pub port: u16,\n pub proxy_origin: String,\n pub last_error: Option<String>,\n pub started_at: Option<String>,\n pub request_count: u64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct LogEntry {\n pub timestamp: String,\n pub level: String,\n pub profile_id: String,\n pub request_id: Option<String>,\n pub message: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProxySnippets {\n pub anthropic_url: String,\n pub openai_url: String,\n pub claude_code_env_unix: String,\n pub claude_code_env_windows: String,\n}\n\npub fn snippets_for(profile: &GatewayProfile) -> ProxySnippets {\n let anthropic_url = profile.anthropic_base_url();\n let openai_url = profile.openai_base_url();\n let m = &profile.model_mapping;\n ProxySnippets {\n anthropic_url: anthropic_url.clone(),\n openai_url,\n claude_code_env_unix: format!(\n \"export ANTHROPIC_BASE_URL={anthropic_url}\\nexport ANTHROPIC_AUTH_TOKEN=<your DeepSeek API Key>\\nexport ANTHROPIC_MODEL={main}\\nexport ANTHROPIC_DEFAULT_OPUS_MODEL={opus}\\nexport ANTHROPIC_DEFAULT_SONNET_MODEL={sonnet}\\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL={haiku}\\nexport CLAUDE_CODE_SUBAGENT_MODEL={subagent}\\nexport CLAUDE_CODE_EFFORT_LEVEL=max\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n claude_code_env_windows: format!(\n \"$env:ANTHROPIC_BASE_URL=\\\"{anthropic_url}\\\"\\n$env:ANTHROPIC_AUTH_TOKEN=\\\"<your DeepSeek API Key>\\\"\\n$env:ANTHROPIC_MODEL=\\\"{main}\\\"\\n$env:ANTHROPIC_DEFAULT_OPUS_MODEL=\\\"{opus}\\\"\\n$env:ANTHROPIC_DEFAULT_SONNET_MODEL=\\\"{sonnet}\\\"\\n$env:ANTHROPIC_DEFAULT_HAIKU_MODEL=\\\"{haiku}\\\"\\n$env:CLAUDE_CODE_SUBAGENT_MODEL=\\\"{subagent}\\\"\\n$env:CLAUDE_CODE_EFFORT_LEVEL=\\\"max\\\"\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn generated_snippets_do_not_embed_real_api_keys() {\n let profile = GatewayProfile::new_default(\"p1\".into(), \"Local\".into(), 17777);\n let snippets = snippets_for(&profile);\n assert!(snippets\n .claude_code_env_unix\n .contains(\"<your DeepSeek API Key>\"));\n assert!(!snippets.claude_code_env_unix.contains(\"sk-\"));\n assert!(snippets\n .claude_code_env_unix\n .contains(\"deepseek-v4-pro[1m]\"));\n }\n}\n"}, "f970669c12c078db99697080a3530174c3cde33d": {"t": 1785759096.0070713, "n": 15933, "h": "0f855d60159e42ab", "c": "use serde::{Deserialize, Serialize};\nuse serde_json::{json, Map, Value};\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ApiSurface {\n Anthropic,\n OpenAi,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ServiceStatusKind {\n Stopped,\n Starting,\n Running,\n Stopping,\n Error,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum LogLevel {\n Error,\n Info,\n Debug,\n}\n\nimpl Default for LogLevel {\n fn default() -> Self {\n Self::Info\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct FeatureFlags {\n pub normalize_adaptive_thinking: bool,\n pub map_effort: bool,\n pub preserve_sse: bool,\n #[serde(default = \"default_true\")]\n pub smooth_streaming_text: bool,\n pub reasoning_replay: bool,\n pub one_m_context_defaults: bool,\n pub redact_sensitive_logs: bool,\n}\n\nfn default_true() -> bool {\n true\n}\n\nimpl Default for FeatureFlags {\n fn default() -> Self {\n Self {\n normalize_adaptive_thinking: true,\n map_effort: true,\n preserve_sse: true,\n smooth_streaming_text: true,\n reasoning_replay: true,\n one_m_context_defaults: true,\n redact_sensitive_logs: true,\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ModelMapping {\n pub main: String,\n pub opus: String,\n pub sonnet: String,\n pub haiku: String,\n pub subagent: String,\n}\n\nimpl Default for ModelMapping {\n fn default() -> Self {\n Self {\n main: \"deepseek-v4-pro[1m]\".into(),\n opus: \"deepseek-v4-pro[1m]\".into(),\n sonnet: \"deepseek-v4-pro[1m]\".into(),\n haiku: \"deepseek-v4-flash\".into(),\n subagent: \"deepseek-v4-flash\".into(),\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct GatewayProfile {\n pub id: String,\n pub name: String,\n pub port: u16,\n pub upstream_base_url: String,\n #[serde(default, skip_serializing_if = \"Option::is_none\")]\n pub api_key: Option<String>,\n pub enabled_surfaces: Vec<ApiSurface>,\n pub model_mapping: ModelMapping,\n pub timeout_seconds: u64,\n pub log_level: LogLevel,\n pub features: FeatureFlags,\n}\n\nimpl GatewayProfile {\n pub fn new_default(id: String, name: String, port: u16) -> Self {\n Self {\n id,\n name,\n port,\n upstream_base_url: \"https://api.deepseek.com\".into(),\n api_key: None,\n enabled_surfaces: vec![ApiSurface::Anthropic, ApiSurface::OpenAi],\n model_mapping: ModelMapping::default(),\n timeout_seconds: 120,\n log_level: LogLevel::default(),\n features: FeatureFlags::default(),\n }\n }\n\n pub fn validate(&self) -> Result<(), String> {\n let trimmed_name = self.name.trim();\n if trimmed_name.is_empty() {\n return Err(\"Profile name is required\".into());\n }\n if self.port < 1024 {\n return Err(\"Port must be 1024 or higher\".into());\n }\n if self.enabled_surfaces.is_empty() {\n return Err(\"At least one API surface must be enabled\".into());\n }\n if !(self.upstream_base_url.starts_with(\"https://\")\n || self.upstream_base_url.starts_with(\"http://\"))\n {\n return Err(\"Upstream base URL must start with http:// or https://\".into());\n }\n if self.timeout_seconds == 0 || self.timeout_seconds > 600 {\n return Err(\"Timeout must be between 1 and 600 seconds\".into());\n }\n Ok(())\n }\n\n pub fn normalized(mut self) -> Self {\n self.name = self.name.trim().to_string();\n self.upstream_base_url = self\n .upstream_base_url\n .trim()\n .trim_end_matches('/')\n .to_string();\n self.api_key = self.api_key.and_then(|key| {\n let trimmed = key.trim().to_string();\n if trimmed.is_empty() {\n None\n } else {\n Some(trimmed)\n }\n });\n self\n }\n\n pub fn fallback_api_key(&self) -> Option<&str> {\n self.api_key\n .as_deref()\n .map(str::trim)\n .filter(|key| !key.is_empty())\n }\n\n pub fn proxy_origin(&self) -> String {\n format!(\"http://127.0.0.1:{}\", self.port)\n }\n\n pub fn anthropic_base_url(&self) -> String {\n format!(\"{}/anthropic\", self.proxy_origin())\n }\n\n pub fn openai_base_url(&self) -> String {\n self.proxy_origin()\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct AppSettings {\n #[serde(default = \"default_mcp_config\")]\n pub mcp_config: Value,\n #[serde(default)]\n #[serde(skip_serializing_if = \"Vec::is_empty\")]\n pub mcp_services: Vec<McpServiceConfig>,\n #[serde(default)]\n pub skills: Vec<SkillConfig>,\n}\n\nimpl Default for AppSettings {\n fn default() -> Self {\n Self {\n mcp_config: default_mcp_config(),\n mcp_services: Vec::new(),\n skills: Vec::new(),\n }\n }\n}\n\nimpl AppSettings {\n pub fn normalized(mut self) -> Self {\n let legacy_services = self\n .mcp_services\n .into_iter()\n .map(McpServiceConfig::normalized)\n .filter(|item| !item.name.is_empty() || !item.command.is_empty())\n .collect::<Vec<_>>();\n self.mcp_config = normalize_mcp_config(self.mcp_config, &legacy_services);\n self.mcp_services = Vec::new();\n self.skills = self\n .skills\n .into_iter()\n .map(SkillConfig::normalized)\n .filter(|item| !item.name.is_empty() || !item.instructions.is_empty())\n .collect();\n self\n }\n\n pub fn validate(&self) -> Result<(), String> {\n if !self.mcp_config.is_object() {\n return Err(\"MCP config must be a JSON object\".into());\n }\n for skill in &self.skills {\n if skill.enabled && skill.name.trim().is_empty() {\n return Err(\"Enabled skills must have a name\".into());\n }\n if skill.enabled && skill.instructions.trim().is_empty() {\n return Err(\"Enabled skills must have instructions\".into());\n }\n }\n Ok(())\n }\n\n pub fn network_request_mcp_enabled(&self) -> bool {\n mcp_server_enabled(&self.mcp_config, \"network-request\")\n || mcp_server_enabled(&self.mcp_config, \"network_request\")\n || self\n .mcp_config\n .pointer(\"/builtin/networkRequest/enabled\")\n .and_then(Value::as_bool)\n .unwrap_or(false)\n }\n\n pub fn request_context(&self) -> Option<String> {\n let services = mcp_server_summaries(&self.mcp_config);\n let skills = self\n .skills\n .iter()\n .filter(|item| item.enabled)\n .collect::<Vec<_>>();\n if services.is_empty() && skills.is_empty() {\n return None;\n }\n\n let mut lines = Vec::from([\n \"DSP-deepseekPartner configured context.\".to_string(),\n \"Use these entries as user-configured guidance for DeepSeek-compatible clients.\"\n .to_string(),\n ]);\n if !skills.is_empty() {\n lines.push(\"Skills:\".to_string());\n for skill in skills {\n let description = if skill.description.is_empty() {\n String::new()\n } else {\n format!(\" - {}\", skill.description)\n };\n lines.push(format!(\"- {}{description}\", skill.name));\n lines.push(format!(\" Instructions: {}\", skill.instructions));\n }\n }\n if !services.is_empty() {\n lines.push(\"MCP services:\".to_string());\n lines.push(\n \"Enabled MCP definitions are available to DSP-deepseekPartner where supported.\"\n .to_string(),\n );\n for service in &services {\n lines.push(format!(\"- {service}\"));\n }\n }\n Some(lines.join(\"\\n\"))\n }\n}\n\nfn default_mcp_config() -> Value {\n json!({\n \"mcpServers\": {\n \"network-request\": {\n \"type\": \"builtin\",\n \"enabled\": true,\n \"tool\": \"network_request\",\n \"description\": \"HTTP/HTTPS request helper executed by DSP-deepseekPartner\"\n }\n }\n })\n}\n\nfn normalize_mcp_config(config: Value, legacy_services: &[McpServiceConfig]) -> Value {\n let mut config = if config.is_null() {\n default_mcp_config()\n } else {\n config\n };\n if !config.is_object() {\n return config;\n }\n if !legacy_services.is_empty() {\n let root = config.as_object_mut().expect(\"object checked above\");\n let servers = root\n .entry(\"mcpServers\")\n .or_insert_with(|| Value::Object(Map::new()));\n if let Some(servers) = servers.as_object_mut() {\n for service in legacy_services {\n servers.insert(\n service.name.clone(),\n json!({\n \"command\": service.command,\n \"args\": split_legacy_args(&service.args),\n \"env\": parse_legacy_env(&service.env),\n \"description\": service.description,\n \"enabled\": service.enabled\n }),\n );\n }\n }\n }\n config\n}\n\nfn split_legacy_args(args: &str) -> Vec<String> {\n args.split_whitespace().map(str::to_string).collect()\n}\n\nfn parse_legacy_env(env: &str) -> Map<String, Value> {\n env.lines()\n .filter_map(|line| line.split_once('='))\n .map(|(key, value)| {\n (\n key.trim().to_string(),\n Value::String(value.trim().to_string()),\n )\n })\n .collect()\n}\n\nfn mcp_server_enabled(config: &Value, name: &str) -> bool {\n config\n .pointer(&format!(\"/mcpServers/{name}/enabled\"))\n .and_then(Value::as_bool)\n .unwrap_or(false)\n}\n\nfn mcp_server_summaries(config: &Value) -> Vec<String> {\n let Some(servers) = config.get(\"mcpServers\").and_then(Value::as_object) else {\n return Vec::new();\n };\n servers\n .iter()\n .filter(|(_, value)| {\n value\n .get(\"enabled\")\n .and_then(Value::as_bool)\n .unwrap_or(true)\n })\n .map(|(name, value)| {\n let description = value\n .get(\"description\")\n .and_then(Value::as_str)\n .filter(|item| !item.trim().is_empty())\n .map(|item| format!(\" - {}\", item.trim()))\n .unwrap_or_default();\n let command = value\n .get(\"command\")\n .and_then(Value::as_str)\n .filter(|item| !item.trim().is_empty())\n .or_else(|| value.get(\"type\").and_then(Value::as_str))\n .unwrap_or(\"configured\");\n format!(\"{name}: {command}{description}\")\n })\n .collect()\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct McpServiceConfig {\n pub id: String,\n pub name: String,\n pub command: String,\n #[serde(default)]\n pub args: String,\n #[serde(default)]\n pub env: String,\n #[serde(default)]\n pub description: String,\n #[serde(default = \"default_true\")]\n pub enabled: bool,\n}\n\nimpl McpServiceConfig {\n fn normalized(mut self) -> Self {\n self.id = self.id.trim().to_string();\n self.name = self.name.trim().to_string();\n self.command = self.command.trim().to_string();\n self.args = self.args.trim().to_string();\n self.env = self.env.trim().to_string();\n self.description = self.description.trim().to_string();\n self\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct SkillConfig {\n pub id: String,\n pub name: String,\n #[serde(default)]\n pub description: String,\n pub instructions: String,\n #[serde(default = \"default_true\")]\n pub enabled: bool,\n}\n\nimpl SkillConfig {\n fn normalized(mut self) -> Self {\n self.id = self.id.trim().to_string();\n self.name = self.name.trim().to_string();\n self.description = self.description.trim().to_string();\n self.instructions = self.instructions.trim().to_string();\n self\n }\n}\n\npub fn canonical_model_id(model: &str) -> String {\n match model {\n \"deepseek-v4-pro[1m]\" => \"deepseek-v4-pro\".to_string(),\n \"deepseek-v4-flash[1m]\" => \"deepseek-v4-flash\".to_string(),\n other => other.to_string(),\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProfileStatus {\n pub id: String,\n pub status: ServiceStatusKind,\n pub port: u16,\n pub proxy_origin: String,\n pub last_error: Option<String>,\n pub started_at: Option<String>,\n pub request_count: u64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct LogEntry {\n pub timestamp: String,\n pub level: String,\n pub profile_id: String,\n pub request_id: Option<String>,\n pub message: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProxySnippets {\n pub anthropic_url: String,\n pub openai_url: String,\n pub claude_code_env_unix: String,\n pub claude_code_env_windows: String,\n}\n\npub fn snippets_for(profile: &GatewayProfile) -> ProxySnippets {\n let anthropic_url = profile.anthropic_base_url();\n let openai_url = profile.openai_base_url();\n let m = &profile.model_mapping;\n ProxySnippets {\n anthropic_url: anthropic_url.clone(),\n openai_url,\n claude_code_env_unix: format!(\n \"export ANTHROPIC_BASE_URL={anthropic_url}\\nexport ANTHROPIC_AUTH_TOKEN=<your DeepSeek API Key>\\nexport ANTHROPIC_MODEL={main}\\nexport ANTHROPIC_DEFAULT_OPUS_MODEL={opus}\\nexport ANTHROPIC_DEFAULT_SONNET_MODEL={sonnet}\\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL={haiku}\\nexport CLAUDE_CODE_SUBAGENT_MODEL={subagent}\\nexport CLAUDE_CODE_EFFORT_LEVEL=max\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n claude_code_env_windows: format!(\n \"$env:ANTHROPIC_BASE_URL=\\\"{anthropic_url}\\\"\\n$env:ANTHROPIC_AUTH_TOKEN=\\\"<your DeepSeek API Key>\\\"\\n$env:ANTHROPIC_MODEL=\\\"{main}\\\"\\n$env:ANTHROPIC_DEFAULT_OPUS_MODEL=\\\"{opus}\\\"\\n$env:ANTHROPIC_DEFAULT_SONNET_MODEL=\\\"{sonnet}\\\"\\n$env:ANTHROPIC_DEFAULT_HAIKU_MODEL=\\\"{haiku}\\\"\\n$env:CLAUDE_CODE_SUBAGENT_MODEL=\\\"{subagent}\\\"\\n$env:CLAUDE_CODE_EFFORT_LEVEL=\\\"max\\\"\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn generated_snippets_do_not_embed_real_api_keys() {\n let profile = GatewayProfile::new_default(\"p1\".into(), \"Local\".into(), 17777);\n let snippets = snippets_for(&profile);\n assert!(snippets\n .claude_code_env_unix\n .contains(\"<your DeepSeek API Key>\"));\n assert!(!snippets.claude_code_env_unix.contains(\"sk-\"));\n assert!(snippets\n .claude_code_env_unix\n .contains(\"deepseek-v4-pro[1m]\"));\n }\n}\n"}, "86c5dfb6573583d77ab2f8e55f8ed6545963fefe": {"t": 1785759097.739508, "n": 12882, "h": "0abcbad48ba08bb7", "c": "use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ApiSurface {\n Anthropic,\n OpenAi,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ServiceStatusKind {\n Stopped,\n Starting,\n Running,\n Stopping,\n Error,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum LogLevel {\n Error,\n Info,\n Debug,\n}\n\nimpl Default for LogLevel {\n fn default() -> Self {\n Self::Info\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct FeatureFlags {\n pub normalize_adaptive_thinking: bool,\n pub map_effort: bool,\n pub preserve_sse: bool,\n #[serde(default = \"default_true\")]\n pub smooth_streaming_text: bool,\n pub reasoning_replay: bool,\n pub one_m_context_defaults: bool,\n pub redact_sensitive_logs: bool,\n}\n\nfn default_true() -> bool {\n true\n}\n\nimpl Default for FeatureFlags {\n fn default() -> Self {\n Self {\n normalize_adaptive_thinking: true,\n map_effort: true,\n preserve_sse: true,\n smooth_streaming_text: true,\n reasoning_replay: true,\n one_m_context_defaults: true,\n redact_sensitive_logs: true,\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ModelMapping {\n pub main: String,\n pub opus: String,\n pub sonnet: String,\n pub haiku: String,\n pub subagent: String,\n}\n\nimpl Default for ModelMapping {\n fn default() -> Self {\n Self {\n main: \"deepseek-v4-pro[1m]\".into(),\n opus: \"deepseek-v4-pro[1m]\".into(),\n sonnet: \"deepseek-v4-pro[1m]\".into(),\n haiku: \"deepseek-v4-flash\".into(),\n subagent: \"deepseek-v4-flash\".into(),\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct GatewayProfile {\n pub id: String,\n pub name: String,\n pub port: u16,\n pub upstream_base_url: String,\n #[serde(default, skip_serializing_if = \"Option::is_none\")]\n pub api_key: Option<String>,\n pub enabled_surfaces: Vec<ApiSurface>,\n pub model_mapping: ModelMapping,\n pub timeout_seconds: u64,\n pub log_level: LogLevel,\n pub features: FeatureFlags,\n}\n\nimpl GatewayProfile {\n pub fn new_default(id: String, name: String, port: u16) -> Self {\n Self {\n id,\n name,\n port,\n upstream_base_url: \"https://api.deepseek.com\".into(),\n api_key: None,\n enabled_surfaces: vec![ApiSurface::Anthropic, ApiSurface::OpenAi],\n model_mapping: ModelMapping::default(),\n timeout_seconds: 120,\n log_level: LogLevel::default(),\n features: FeatureFlags::default(),\n }\n }\n\n pub fn validate(&self) -> Result<(), String> {\n let trimmed_name = self.name.trim();\n if trimmed_name.is_empty() {\n return Err(\"Profile name is required\".into());\n }\n if self.port < 1024 {\n return Err(\"Port must be 1024 or higher\".into());\n }\n if self.enabled_surfaces.is_empty() {\n return Err(\"At least one API surface must be enabled\".into());\n }\n if !(self.upstream_base_url.starts_with(\"https://\")\n || self.upstream_base_url.starts_with(\"http://\"))\n {\n return Err(\"Upstream base URL must start with http:// or https://\".into());\n }\n if self.timeout_seconds == 0 || self.timeout_seconds > 600 {\n return Err(\"Timeout must be between 1 and 600 seconds\".into());\n }\n Ok(())\n }\n\n pub fn normalized(mut self) -> Self {\n self.name = self.name.trim().to_string();\n self.upstream_base_url = self\n .upstream_base_url\n .trim()\n .trim_end_matches('/')\n .to_string();\n self.api_key = self.api_key.and_then(|key| {\n let trimmed = key.trim().to_string();\n if trimmed.is_empty() {\n None\n } else {\n Some(trimmed)\n }\n });\n self\n }\n\n pub fn fallback_api_key(&self) -> Option<&str> {\n self.api_key\n .as_deref()\n .map(str::trim)\n .filter(|key| !key.is_empty())\n }\n\n pub fn proxy_origin(&self) -> String {\n format!(\"http://127.0.0.1:{}\", self.port)\n }\n\n pub fn anthropic_base_url(&self) -> String {\n format!(\"{}/anthropic\", self.proxy_origin())\n }\n\n pub fn openai_base_url(&self) -> String {\n self.proxy_origin()\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, Default)]\n#[serde(rename_all = \"camelCase\")]\npub struct AppSettings {\n #[serde(default)]\n pub mcp_services: Vec<McpServiceConfig>,\n #[serde(default)]\n pub skills: Vec<SkillConfig>,\n}\n\nimpl AppSettings {\n pub fn normalized(mut self) -> Self {\n self.mcp_services = self\n .mcp_services\n .into_iter()\n .map(McpServiceConfig::normalized)\n .filter(|item| !item.name.is_empty() || !item.command.is_empty())\n .collect();\n self.skills = self\n .skills\n .into_iter()\n .map(SkillConfig::normalized)\n .filter(|item| !item.name.is_empty() || !item.instructions.is_empty())\n .collect();\n self\n }\n\n pub fn validate(&self) -> Result<(), String> {\n for service in &self.mcp_services {\n if service.enabled && service.name.trim().is_empty() {\n return Err(\"Enabled MCP services must have a name\".into());\n }\n if service.enabled && service.command.trim().is_empty() {\n return Err(\"Enabled MCP services must have a command\".into());\n }\n }\n for skill in &self.skills {\n if skill.enabled && skill.name.trim().is_empty() {\n return Err(\"Enabled skills must have a name\".into());\n }\n if skill.enabled && skill.instructions.trim().is_empty() {\n return Err(\"Enabled skills must have instructions\".into());\n }\n }\n Ok(())\n }\n\n pub fn request_context(&self) -> Option<String> {\n let services = self\n .mcp_services\n .iter()\n .filter(|item| item.enabled)\n .collect::<Vec<_>>();\n let skills = self\n .skills\n .iter()\n .filter(|item| item.enabled)\n .collect::<Vec<_>>();\n if services.is_empty() && skills.is_empty() {\n return None;\n }\n\n let mut lines = Vec::from([\n \"DSP-deepseekPartner configured context.\".to_string(),\n \"Use these entries as user-configured guidance for DeepSeek-compatible clients.\"\n .to_string(),\n ]);\n if !skills.is_empty() {\n lines.push(\"Skills:\".to_string());\n for skill in skills {\n let description = if skill.description.is_empty() {\n String::new()\n } else {\n format!(\" - {}\", skill.description)\n };\n lines.push(format!(\"- {}{description}\", skill.name));\n lines.push(format!(\" Instructions: {}\", skill.instructions));\n }\n }\n if !services.is_empty() {\n lines.push(\"MCP services:\".to_string());\n lines.push(\n \"These are configured MCP service definitions; tool execution still depends on the client/runtime exposing them.\"\n .to_string(),\n );\n for service in services {\n let args = if service.args.is_empty() {\n String::new()\n } else {\n format!(\" {}\", service.args)\n };\n let description = if service.description.is_empty() {\n String::new()\n } else {\n format!(\" - {}\", service.description)\n };\n lines.push(format!(\n \"- {}: {}{args}{description}\",\n service.name, service.command\n ));\n }\n }\n Some(lines.join(\"\\n\"))\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct McpServiceConfig {\n pub id: String,\n pub name: String,\n pub command: String,\n #[serde(default)]\n pub args: String,\n #[serde(default)]\n pub env: String,\n #[serde(default)]\n pub description: String,\n #[serde(default = \"default_true\")]\n pub enabled: bool,\n}\n\nimpl McpServiceConfig {\n fn normalized(mut self) -> Self {\n self.id = self.id.trim().to_string();\n self.name = self.name.trim().to_string();\n self.command = self.command.trim().to_string();\n self.args = self.args.trim().to_string();\n self.env = self.env.trim().to_string();\n self.description = self.description.trim().to_string();\n self\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct SkillConfig {\n pub id: String,\n pub name: String,\n #[serde(default)]\n pub description: String,\n pub instructions: String,\n #[serde(default = \"default_true\")]\n pub enabled: bool,\n}\n\nimpl SkillConfig {\n fn normalized(mut self) -> Self {\n self.id = self.id.trim().to_string();\n self.name = self.name.trim().to_string();\n self.description = self.description.trim().to_string();\n self.instructions = self.instructions.trim().to_string();\n self\n }\n}\n\npub fn canonical_model_id(model: &str) -> String {\n match model {\n \"deepseek-v4-pro[1m]\" => \"deepseek-v4-pro\".to_string(),\n \"deepseek-v4-flash[1m]\" => \"deepseek-v4-flash\".to_string(),\n other => other.to_string(),\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProfileStatus {\n pub id: String,\n pub status: ServiceStatusKind,\n pub port: u16,\n pub proxy_origin: String,\n pub last_error: Option<String>,\n pub started_at: Option<String>,\n pub request_count: u64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct LogEntry {\n pub timestamp: String,\n pub level: String,\n pub profile_id: String,\n pub request_id: Option<String>,\n pub message: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProxySnippets {\n pub anthropic_url: String,\n pub openai_url: String,\n pub claude_code_env_unix: String,\n pub claude_code_env_windows: String,\n}\n\npub fn snippets_for(profile: &GatewayProfile) -> ProxySnippets {\n let anthropic_url = profile.anthropic_base_url();\n let openai_url = profile.openai_base_url();\n let m = &profile.model_mapping;\n ProxySnippets {\n anthropic_url: anthropic_url.clone(),\n openai_url,\n claude_code_env_unix: format!(\n \"export ANTHROPIC_BASE_URL={anthropic_url}\\nexport ANTHROPIC_AUTH_TOKEN=<your DeepSeek API Key>\\nexport ANTHROPIC_MODEL={main}\\nexport ANTHROPIC_DEFAULT_OPUS_MODEL={opus}\\nexport ANTHROPIC_DEFAULT_SONNET_MODEL={sonnet}\\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL={haiku}\\nexport CLAUDE_CODE_SUBAGENT_MODEL={subagent}\\nexport CLAUDE_CODE_EFFORT_LEVEL=max\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n claude_code_env_windows: format!(\n \"$env:ANTHROPIC_BASE_URL=\\\"{anthropic_url}\\\"\\n$env:ANTHROPIC_AUTH_TOKEN=\\\"<your DeepSeek API Key>\\\"\\n$env:ANTHROPIC_MODEL=\\\"{main}\\\"\\n$env:ANTHROPIC_DEFAULT_OPUS_MODEL=\\\"{opus}\\\"\\n$env:ANTHROPIC_DEFAULT_SONNET_MODEL=\\\"{sonnet}\\\"\\n$env:ANTHROPIC_DEFAULT_HAIKU_MODEL=\\\"{haiku}\\\"\\n$env:CLAUDE_CODE_SUBAGENT_MODEL=\\\"{subagent}\\\"\\n$env:CLAUDE_CODE_EFFORT_LEVEL=\\\"max\\\"\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn generated_snippets_do_not_embed_real_api_keys() {\n let profile = GatewayProfile::new_default(\"p1\".into(), \"Local\".into(), 17777);\n let snippets = snippets_for(&profile);\n assert!(snippets\n .claude_code_env_unix\n .contains(\"<your DeepSeek API Key>\"));\n assert!(!snippets.claude_code_env_unix.contains(\"sk-\"));\n assert!(snippets\n .claude_code_env_unix\n .contains(\"deepseek-v4-pro[1m]\"));\n }\n}\n"}, "477e32f76c148692457d5bb9bf5fefde7bb24cc9": {"t": 1785759098.9950743, "n": 7025, "h": "2b32640cd81551a3", "c": "use serde::{Deserialize, Serialize};\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ApiSurface {\n Anthropic,\n OpenAi,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum ServiceStatusKind {\n Stopped,\n Starting,\n Running,\n Stopping,\n Error,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]\n#[serde(rename_all = \"camelCase\")]\npub enum LogLevel {\n Error,\n Info,\n Debug,\n}\n\nimpl Default for LogLevel {\n fn default() -> Self {\n Self::Info\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct FeatureFlags {\n pub normalize_adaptive_thinking: bool,\n pub map_effort: bool,\n pub preserve_sse: bool,\n #[serde(default = \"default_true\")]\n pub smooth_streaming_text: bool,\n pub reasoning_replay: bool,\n pub one_m_context_defaults: bool,\n pub redact_sensitive_logs: bool,\n}\n\nfn default_true() -> bool {\n true\n}\n\nimpl Default for FeatureFlags {\n fn default() -> Self {\n Self {\n normalize_adaptive_thinking: true,\n map_effort: true,\n preserve_sse: true,\n smooth_streaming_text: true,\n reasoning_replay: true,\n one_m_context_defaults: true,\n redact_sensitive_logs: true,\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ModelMapping {\n pub main: String,\n pub opus: String,\n pub sonnet: String,\n pub haiku: String,\n pub subagent: String,\n}\n\nimpl Default for ModelMapping {\n fn default() -> Self {\n Self {\n main: \"deepseek-v4-pro[1m]\".into(),\n opus: \"deepseek-v4-pro[1m]\".into(),\n sonnet: \"deepseek-v4-pro[1m]\".into(),\n haiku: \"deepseek-v4-flash\".into(),\n subagent: \"deepseek-v4-flash\".into(),\n }\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct GatewayProfile {\n pub id: String,\n pub name: String,\n pub port: u16,\n pub upstream_base_url: String,\n pub enabled_surfaces: Vec<ApiSurface>,\n pub model_mapping: ModelMapping,\n pub timeout_seconds: u64,\n pub log_level: LogLevel,\n pub features: FeatureFlags,\n}\n\nimpl GatewayProfile {\n pub fn new_default(id: String, name: String, port: u16) -> Self {\n Self {\n id,\n name,\n port,\n upstream_base_url: \"https://api.deepseek.com\".into(),\n enabled_surfaces: vec![ApiSurface::Anthropic, ApiSurface::OpenAi],\n model_mapping: ModelMapping::default(),\n timeout_seconds: 120,\n log_level: LogLevel::default(),\n features: FeatureFlags::default(),\n }\n }\n\n pub fn validate(&self) -> Result<(), String> {\n let trimmed_name = self.name.trim();\n if trimmed_name.is_empty() {\n return Err(\"Profile name is required\".into());\n }\n if self.port < 1024 {\n return Err(\"Port must be 1024 or higher\".into());\n }\n if self.enabled_surfaces.is_empty() {\n return Err(\"At least one API surface must be enabled\".into());\n }\n if !(self.upstream_base_url.starts_with(\"https://\")\n || self.upstream_base_url.starts_with(\"http://\"))\n {\n return Err(\"Upstream base URL must start with http:// or https://\".into());\n }\n if self.timeout_seconds == 0 || self.timeout_seconds > 600 {\n return Err(\"Timeout must be between 1 and 600 seconds\".into());\n }\n Ok(())\n }\n\n pub fn proxy_origin(&self) -> String {\n format!(\"http://127.0.0.1:{}\", self.port)\n }\n\n pub fn anthropic_base_url(&self) -> String {\n format!(\"{}/anthropic\", self.proxy_origin())\n }\n\n pub fn openai_base_url(&self) -> String {\n self.proxy_origin()\n }\n}\n\npub fn canonical_model_id(model: &str) -> String {\n match model {\n \"deepseek-v4-pro[1m]\" => \"deepseek-v4-pro\".to_string(),\n \"deepseek-v4-flash[1m]\" => \"deepseek-v4-flash\".to_string(),\n other => other.to_string(),\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProfileStatus {\n pub id: String,\n pub status: ServiceStatusKind,\n pub port: u16,\n pub proxy_origin: String,\n pub last_error: Option<String>,\n pub started_at: Option<String>,\n pub request_count: u64,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct LogEntry {\n pub timestamp: String,\n pub level: String,\n pub profile_id: String,\n pub request_id: Option<String>,\n pub message: String,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n#[serde(rename_all = \"camelCase\")]\npub struct ProxySnippets {\n pub anthropic_url: String,\n pub openai_url: String,\n pub claude_code_env_unix: String,\n pub claude_code_env_windows: String,\n}\n\npub fn snippets_for(profile: &GatewayProfile) -> ProxySnippets {\n let anthropic_url = profile.anthropic_base_url();\n let openai_url = profile.openai_base_url();\n let m = &profile.model_mapping;\n ProxySnippets {\n anthropic_url: anthropic_url.clone(),\n openai_url,\n claude_code_env_unix: format!(\n \"export ANTHROPIC_BASE_URL={anthropic_url}\\nexport ANTHROPIC_AUTH_TOKEN=<your DeepSeek API Key>\\nexport ANTHROPIC_MODEL={main}\\nexport ANTHROPIC_DEFAULT_OPUS_MODEL={opus}\\nexport ANTHROPIC_DEFAULT_SONNET_MODEL={sonnet}\\nexport ANTHROPIC_DEFAULT_HAIKU_MODEL={haiku}\\nexport CLAUDE_CODE_SUBAGENT_MODEL={subagent}\\nexport CLAUDE_CODE_EFFORT_LEVEL=max\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n claude_code_env_windows: format!(\n \"$env:ANTHROPIC_BASE_URL=\\\"{anthropic_url}\\\"\\n$env:ANTHROPIC_AUTH_TOKEN=\\\"<your DeepSeek API Key>\\\"\\n$env:ANTHROPIC_MODEL=\\\"{main}\\\"\\n$env:ANTHROPIC_DEFAULT_OPUS_MODEL=\\\"{opus}\\\"\\n$env:ANTHROPIC_DEFAULT_SONNET_MODEL=\\\"{sonnet}\\\"\\n$env:ANTHROPIC_DEFAULT_HAIKU_MODEL=\\\"{haiku}\\\"\\n$env:CLAUDE_CODE_SUBAGENT_MODEL=\\\"{subagent}\\\"\\n$env:CLAUDE_CODE_EFFORT_LEVEL=\\\"max\\\"\",\n main = m.main,\n opus = m.opus,\n sonnet = m.sonnet,\n haiku = m.haiku,\n subagent = m.subagent\n ),\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn generated_snippets_do_not_embed_real_api_keys() {\n let profile = GatewayProfile::new_default(\"p1\".into(), \"Local\".into(), 17777);\n let snippets = snippets_for(&profile);\n assert!(snippets\n .claude_code_env_unix\n .contains(\"<your DeepSeek API Key>\"));\n assert!(!snippets.claude_code_env_unix.contains(\"sk-\"));\n assert!(snippets\n .claude_code_env_unix\n .contains(\"deepseek-v4-pro[1m]\"));\n }\n}\n"}}}}