1 line
23 KiB
JSON
1 line
23 KiB
JSON
{"repo": "buffer121328/assistant", "files": {"backend/infrastructure/settings/config.py": {"ca9de9bb2ddb24970b31c7e938987d2e358f91b5": {"t": 1785745419.203891, "n": 5505, "h": "511c328d59731b1b", "c": "from pathlib import Path\nfrom typing import Literal\n\nfrom pydantic import SecretStr, field_validator\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\nclass Settings(BaseSettings):\n \"\"\"表示 处理 settings 的后端数据结构或服务对象。\"\"\"\n\n app_env: str = \"local\"\n log_level: str = \"INFO\"\n service_name: str = \"assistant-api\"\n local_api_auth_required: bool = False\n local_api_token: SecretStr = SecretStr(\"\")\n credential_master_key: SecretStr = SecretStr(\"\")\n database_url: str = \"postgresql+asyncpg://placeholder\"\n redis_url: str = \"redis://placeholder\"\n sentry_dsn: str | None = None\n langfuse_public_key: str | None = None\n langfuse_secret_key: str | None = None\n langfuse_base_url: str | None = None\n langbot_webhook_secret: str = \"placeholder-langbot-webhook-secret\"\n langbot_api_base_url: str = \"https://langbot.invalid\"\n langbot_api_key: str = \"placeholder-langbot-api-key\"\n langbot_send_timeout_seconds: float = 10.0\n deepseek_api_key: str = \"placeholder-deepseek-api-key\"\n deepseek_base_url: str = \"https://deepseek.invalid/v1\"\n deepseek_light_model: str = \"deepseek-light-placeholder\"\n deepseek_standard_model: str = \"deepseek-standard-placeholder\"\n models_timeout_seconds: float = 10.0\n models_retry_attempts: int = 2\n models_nodes_json: str = \"\"\n tavily_base_url: str = \"https://tavily.invalid\"\n tavily_api_key: str = \"placeholder-tavily-api-key\"\n tavily_timeout_seconds: float = 10.0\n tavily_max_results: int = 5\n search_provider_order: str = \"tavily,brave,duckduckgo\"\n brave_search_api_key: str = \"\"\n brave_search_base_url: str = \"https://api.search.brave.com/res/v1/web/search\"\n duckduckgo_search_enabled: bool = False\n duckduckgo_search_base_url: str = \"https://api.duckduckgo.com/\"\n search_fallback_on_empty: bool = True\n search_provider_timeout_seconds: float | None = None\n running_task_timeout_seconds: float = 300.0\n pending_task_compensation_delay_seconds: float = 120.0\n scheduler_maintenance_interval_seconds: float = 300.0\n managed_skills_root: Path = Path(\"var/skills\")\n managed_prompts_root: Path = Path(\"var/prompts\")\n skill_packages_root: Path = Path(\"var/skill-packages\")\n artifacts_root: Path = Path(\"var/artifacts\")\n session_workspace_root: Path = Path(\"var/workspace/sessions\")\n workspace_context_root: Path = Path(\".\")\n workspace_context_enabled: bool = True\n workspace_context_deny_globs: str = \".env,.env.*,**/.env,**/.env.*,.git/**,**/.git/**,node_modules/**,**/node_modules/**,__pycache__/**,**/__pycache__/**,*.pem,**/*.pem,*.key,**/*.key,*.p12,**/*.p12,*.sqlite,**/*.sqlite,*.db,**/*.db\"\n workspace_context_max_file_bytes: int = 200_000\n workspace_context_max_results: int = 50\n readonly_shell_enabled: bool = False\n readonly_shell_timeout_seconds: float = 10.0\n readonly_shell_max_output_chars: int = 50_000\n knowledge_root: Path = Path(\"var/knowledge\")\n browser_enabled: bool = False\n browser_timeout_seconds: float = 20.0\n browser_max_text_chars: int = 50_000\n sandbox_provider: Literal[\"none\", \"docker\"] = \"none\"\n shell_exec_enabled: bool = False\n sandbox_workspace_root: Path = Path(\"var/sandbox\")\n sandbox_docker_image: str = \"\"\n sandbox_docker_allowed_images: str = \"\"\n sandbox_enabled: bool = False\n sandbox_image: str = \"\"\n sandbox_allowed_images: str = \"\"\n sandbox_timeout_seconds: float = 30.0\n subagent_enabled: bool = True\n subagent_max_count: int = 3\n subagent_concurrency: int = 2\n subagent_timeout_seconds: float = 30.0\n mem0_config_path: Path | None = None\n mem0_search_limit: int = 5\n quality_judge_sample_rate: float = 0.0\n quality_judge_policy_version: str = \"judge-v1\"\n quality_judge_threshold: float = 0.6\n\n model_config = SettingsConfigDict(env_file=\".env\", extra=\"ignore\")\n\n @field_validator(\"search_provider_timeout_seconds\", mode=\"before\")\n @classmethod\n def blank_search_provider_timeout_is_none(cls, value: object) -> object:\n \"\"\"处理 blank search provider timeout is none。\n\n Args:\n value: value 参数。\n \"\"\"\n if isinstance(value, str) and not value.strip():\n return None\n return value\n\n @property\n def effective_sandbox_provider(self) -> Literal[\"none\", \"docker\"]:\n \"\"\"处理 effective sandbox provider。\"\"\"\n if self.sandbox_provider != \"none\":\n return self.sandbox_provider\n if self.sandbox_enabled:\n return \"docker\"\n return \"none\"\n\n @property\n def effective_shell_exec_enabled(self) -> bool:\n \"\"\"处理 effective shell exec enabled。\"\"\"\n return self.shell_exec_enabled or self.sandbox_enabled\n\n @property\n def effective_sandbox_docker_image(self) -> str:\n \"\"\"处理 effective sandbox docker image。\"\"\"\n return self.sandbox_docker_image or self.sandbox_image\n\n @property\n def effective_sandbox_docker_allowed_images(self) -> str:\n \"\"\"处理 effective sandbox docker allowed images。\"\"\"\n return self.sandbox_docker_allowed_images or self.sandbox_allowed_images\n\n @property\n def effective_sandbox_docker_allowed_images_tuple(self) -> tuple[str, ...]:\n \"\"\"处理 effective sandbox docker allowed images tuple。\"\"\"\n return tuple(\n item.strip()\n for item in self.effective_sandbox_docker_allowed_images.split(\",\")\n if item.strip()\n )\n\n\ndef load_settings() -> Settings:\n \"\"\"加载 settings。\"\"\"\n return Settings()\n"}, "ed84a2ae8920b7dabcfd785eda37c8ba52d0ad28": {"t": 1785752982.8413131, "n": 5505, "h": "511c328d59731b1b", "c": "from pathlib import Path\nfrom typing import Literal\n\nfrom pydantic import SecretStr, field_validator\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\n\n\nclass Settings(BaseSettings):\n \"\"\"表示 处理 settings 的后端数据结构或服务对象。\"\"\"\n\n app_env: str = \"local\"\n log_level: str = \"INFO\"\n service_name: str = \"assistant-api\"\n local_api_auth_required: bool = False\n local_api_token: SecretStr = SecretStr(\"\")\n credential_master_key: SecretStr = SecretStr(\"\")\n database_url: str = \"postgresql+asyncpg://placeholder\"\n redis_url: str = \"redis://placeholder\"\n sentry_dsn: str | None = None\n langfuse_public_key: str | None = None\n langfuse_secret_key: str | None = None\n langfuse_base_url: str | None = None\n langbot_webhook_secret: str = \"placeholder-langbot-webhook-secret\"\n langbot_api_base_url: str = \"https://langbot.invalid\"\n langbot_api_key: str = \"placeholder-langbot-api-key\"\n langbot_send_timeout_seconds: float = 10.0\n deepseek_api_key: str = \"placeholder-deepseek-api-key\"\n deepseek_base_url: str = \"https://deepseek.invalid/v1\"\n deepseek_light_model: str = \"deepseek-light-placeholder\"\n deepseek_standard_model: str = \"deepseek-standard-placeholder\"\n models_timeout_seconds: float = 10.0\n models_retry_attempts: int = 2\n models_nodes_json: str = \"\"\n tavily_base_url: str = \"https://tavily.invalid\"\n tavily_api_key: str = \"placeholder-tavily-api-key\"\n tavily_timeout_seconds: float = 10.0\n tavily_max_results: int = 5\n search_provider_order: str = \"tavily,brave,duckduckgo\"\n brave_search_api_key: str = \"\"\n brave_search_base_url: str = \"https://api.search.brave.com/res/v1/web/search\"\n duckduckgo_search_enabled: bool = False\n duckduckgo_search_base_url: str = \"https://api.duckduckgo.com/\"\n search_fallback_on_empty: bool = True\n search_provider_timeout_seconds: float | None = None\n running_task_timeout_seconds: float = 300.0\n pending_task_compensation_delay_seconds: float = 120.0\n scheduler_maintenance_interval_seconds: float = 300.0\n managed_skills_root: Path = Path(\"var/skills\")\n managed_prompts_root: Path = Path(\"var/prompts\")\n skill_packages_root: Path = Path(\"var/skill-packages\")\n artifacts_root: Path = Path(\"var/artifacts\")\n session_workspace_root: Path = Path(\"var/workspace/sessions\")\n workspace_context_root: Path = Path(\".\")\n workspace_context_enabled: bool = True\n workspace_context_deny_globs: str = \".env,.env.*,**/.env,**/.env.*,.git/**,**/.git/**,node_modules/**,**/node_modules/**,__pycache__/**,**/__pycache__/**,*.pem,**/*.pem,*.key,**/*.key,*.p12,**/*.p12,*.sqlite,**/*.sqlite,*.db,**/*.db\"\n workspace_context_max_file_bytes: int = 200_000\n workspace_context_max_results: int = 50\n readonly_shell_enabled: bool = False\n readonly_shell_timeout_seconds: float = 10.0\n readonly_shell_max_output_chars: int = 50_000\n knowledge_root: Path = Path(\"var/knowledge\")\n browser_enabled: bool = False\n browser_timeout_seconds: float = 20.0\n browser_max_text_chars: int = 50_000\n sandbox_provider: Literal[\"none\", \"docker\"] = \"none\"\n shell_exec_enabled: bool = False\n sandbox_workspace_root: Path = Path(\"var/sandbox\")\n sandbox_docker_image: str = \"\"\n sandbox_docker_allowed_images: str = \"\"\n sandbox_enabled: bool = False\n sandbox_image: str = \"\"\n sandbox_allowed_images: str = \"\"\n sandbox_timeout_seconds: float = 30.0\n subagent_enabled: bool = True\n subagent_max_count: int = 3\n subagent_concurrency: int = 2\n subagent_timeout_seconds: float = 30.0\n mem0_config_path: Path | None = None\n mem0_search_limit: int = 5\n quality_judge_sample_rate: float = 0.0\n quality_judge_policy_version: str = \"judge-v1\"\n quality_judge_threshold: float = 0.6\n\n model_config = SettingsConfigDict(env_file=\".env\", extra=\"ignore\")\n\n @field_validator(\"search_provider_timeout_seconds\", mode=\"before\")\n @classmethod\n def blank_search_provider_timeout_is_none(cls, value: object) -> object:\n \"\"\"处理 blank search provider timeout is none。\n\n Args:\n value: value 参数。\n \"\"\"\n if isinstance(value, str) and not value.strip():\n return None\n return value\n\n @property\n def effective_sandbox_provider(self) -> Literal[\"none\", \"docker\"]:\n \"\"\"处理 effective sandbox provider。\"\"\"\n if self.sandbox_provider != \"none\":\n return self.sandbox_provider\n if self.sandbox_enabled:\n return \"docker\"\n return \"none\"\n\n @property\n def effective_shell_exec_enabled(self) -> bool:\n \"\"\"处理 effective shell exec enabled。\"\"\"\n return self.shell_exec_enabled or self.sandbox_enabled\n\n @property\n def effective_sandbox_docker_image(self) -> str:\n \"\"\"处理 effective sandbox docker image。\"\"\"\n return self.sandbox_docker_image or self.sandbox_image\n\n @property\n def effective_sandbox_docker_allowed_images(self) -> str:\n \"\"\"处理 effective sandbox docker allowed images。\"\"\"\n return self.sandbox_docker_allowed_images or self.sandbox_allowed_images\n\n @property\n def effective_sandbox_docker_allowed_images_tuple(self) -> tuple[str, ...]:\n \"\"\"处理 effective sandbox docker allowed images tuple。\"\"\"\n return tuple(\n item.strip()\n for item in self.effective_sandbox_docker_allowed_images.split(\",\")\n if item.strip()\n )\n\n\ndef load_settings() -> Settings:\n \"\"\"加载 settings。\"\"\"\n return Settings()\n"}}, "backend/app/api/routers/model_chat.py": {"ca9de9bb2ddb24970b31c7e938987d2e358f91b5": {"t": 1785745426.9550111, "n": 2542, "h": "243fc8660a123965", "c": "from __future__ import annotations\n\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.api.schemas import (\n ModelChatRequest,\n ModelChatResponse,\n ModelGatewayUsage,\n)\nfrom app.support.errors import AppError\nfrom domain.policies.redaction import sanitize_text\nfrom infrastructure.persistence.database import get_session\nfrom infrastructure.settings.config import Settings\nfrom model_gateway import GatewayMessage, GatewayRequest, GatewayResult, ModelGatewayError\nfrom model_gateway.chat_service import handle_model_chat\n\nrouter = APIRouter()\n\n\n@router.post(\"/internal/models/chat\", response_model=ModelChatResponse)\nasync def chat_with_model(\n payload: ModelChatRequest,\n request: Request,\n session: Annotated[AsyncSession, Depends(get_session)],\n) -> ModelChatResponse:\n \"\"\"Handle an internal model chat request through the model gateway.\"\"\"\n settings: Settings = request.app.state.settings\n gateway_request = GatewayRequest(\n user_id=payload.user_id,\n task_id=payload.task_id,\n task_type=payload.task_type,\n model_class=payload.model_class,\n messages=tuple(\n GatewayMessage(role=message.role, content=message.content)\n for message in payload.messages\n ),\n temperature=payload.temperature,\n max_tokens=payload.max_tokens,\n )\n try:\n result = await handle_model_chat(\n request=gateway_request,\n session=session,\n settings=settings,\n )\n except ModelGatewayError as exc:\n raise _app_error(exc, settings) from exc\n return _response(result)\n\n\ndef _response(result: GatewayResult) -> ModelChatResponse:\n \"\"\"Convert a gateway result into an API response DTO.\"\"\"\n return ModelChatResponse(\n provider=result.provider,\n model=result.model,\n content=result.content,\n usage=ModelGatewayUsage(\n input_tokens=result.usage.input_tokens,\n output_tokens=result.usage.output_tokens,\n ),\n latency_ms=result.latency_ms,\n status=\"succeeded\",\n )\n\n\ndef _app_error(error: ModelGatewayError, settings: Settings) -> AppError:\n \"\"\"Convert gateway errors into API-layer errors without leaking secrets.\"\"\"\n return AppError(\n code=error.code,\n message=sanitize_text(\n error.message,\n extra_sensitive_values=(settings.deepseek_api_key,),\n ),\n status_code=error.status_code,\n )\n"}, "ed84a2ae8920b7dabcfd785eda37c8ba52d0ad28": {"t": 1785752982.8182561, "n": 2542, "h": "243fc8660a123965", "c": "from __future__ import annotations\n\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.api.schemas import (\n ModelChatRequest,\n ModelChatResponse,\n ModelGatewayUsage,\n)\nfrom app.support.errors import AppError\nfrom domain.policies.redaction import sanitize_text\nfrom infrastructure.persistence.database import get_session\nfrom infrastructure.settings.config import Settings\nfrom model_gateway import GatewayMessage, GatewayRequest, GatewayResult, ModelGatewayError\nfrom model_gateway.chat_service import handle_model_chat\n\nrouter = APIRouter()\n\n\n@router.post(\"/internal/models/chat\", response_model=ModelChatResponse)\nasync def chat_with_model(\n payload: ModelChatRequest,\n request: Request,\n session: Annotated[AsyncSession, Depends(get_session)],\n) -> ModelChatResponse:\n \"\"\"Handle an internal model chat request through the model gateway.\"\"\"\n settings: Settings = request.app.state.settings\n gateway_request = GatewayRequest(\n user_id=payload.user_id,\n task_id=payload.task_id,\n task_type=payload.task_type,\n model_class=payload.model_class,\n messages=tuple(\n GatewayMessage(role=message.role, content=message.content)\n for message in payload.messages\n ),\n temperature=payload.temperature,\n max_tokens=payload.max_tokens,\n )\n try:\n result = await handle_model_chat(\n request=gateway_request,\n session=session,\n settings=settings,\n )\n except ModelGatewayError as exc:\n raise _app_error(exc, settings) from exc\n return _response(result)\n\n\ndef _response(result: GatewayResult) -> ModelChatResponse:\n \"\"\"Convert a gateway result into an API response DTO.\"\"\"\n return ModelChatResponse(\n provider=result.provider,\n model=result.model,\n content=result.content,\n usage=ModelGatewayUsage(\n input_tokens=result.usage.input_tokens,\n output_tokens=result.usage.output_tokens,\n ),\n latency_ms=result.latency_ms,\n status=\"succeeded\",\n )\n\n\ndef _app_error(error: ModelGatewayError, settings: Settings) -> AppError:\n \"\"\"Convert gateway errors into API-layer errors without leaking secrets.\"\"\"\n return AppError(\n code=error.code,\n message=sanitize_text(\n error.message,\n extra_sensitive_values=(settings.deepseek_api_key,),\n ),\n status_code=error.status_code,\n )\n"}, "59ca59e0731335d6aae647487a8695b118c6a3df": {"t": 1785752984.2218206, "n": 2498, "h": "7ffb7cee4d2d66e2", "c": "from __future__ import annotations\n\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom app.api.schemas import (\n ModelChatRequest,\n ModelChatResponse,\n ModelGatewayUsage,\n)\nfrom app.support.errors import AppError\nfrom common.redaction import sanitize_text\nfrom infrastructure.database import get_session\nfrom infrastructure.config import Settings\nfrom models import GatewayMessage, GatewayRequest, GatewayResult, ModelGatewayError\nfrom models.chat_service import handle_model_chat\n\nrouter = APIRouter()\n\n\n@router.post(\"/internal/models/chat\", response_model=ModelChatResponse)\nasync def chat_with_model(\n payload: ModelChatRequest,\n request: Request,\n session: Annotated[AsyncSession, Depends(get_session)],\n) -> ModelChatResponse:\n \"\"\"Handle an internal model chat request through the model gateway.\"\"\"\n settings: Settings = request.app.state.settings\n gateway_request = GatewayRequest(\n user_id=payload.user_id,\n task_id=payload.task_id,\n task_type=payload.task_type,\n model_class=payload.model_class,\n messages=tuple(\n GatewayMessage(role=message.role, content=message.content)\n for message in payload.messages\n ),\n temperature=payload.temperature,\n max_tokens=payload.max_tokens,\n )\n try:\n result = await handle_model_chat(\n request=gateway_request,\n session=session,\n settings=settings,\n )\n except ModelGatewayError as exc:\n raise _app_error(exc, settings) from exc\n return _response(result)\n\n\ndef _response(result: GatewayResult) -> ModelChatResponse:\n \"\"\"Convert a gateway result into an API response DTO.\"\"\"\n return ModelChatResponse(\n provider=result.provider,\n model=result.model,\n content=result.content,\n usage=ModelGatewayUsage(\n input_tokens=result.usage.input_tokens,\n output_tokens=result.usage.output_tokens,\n ),\n latency_ms=result.latency_ms,\n status=\"succeeded\",\n )\n\n\ndef _app_error(error: ModelGatewayError, settings: Settings) -> AppError:\n \"\"\"Convert gateway errors into API-layer errors without leaking secrets.\"\"\"\n return AppError(\n code=error.code,\n message=sanitize_text(\n error.message,\n extra_sensitive_values=(settings.deepseek_api_key,),\n ),\n status_code=error.status_code,\n )\n"}, "916bfa724f3e50a1fde421402258094042495c2c": {"t": 1785752985.4392939, "n": 868, "h": "84ac7acf1027e4bc", "c": "from __future__ import annotations\n\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom infrastructure.database import get_session\nfrom models.chat_service import handle_model_chat\nfrom app.api.schemas import (\n ModelChatRequest,\n ModelChatResponse,\n)\n\nrouter = APIRouter()\n\n\n@router.post(\"/internal/models/chat\", response_model=ModelChatResponse)\nasync def chat_with_model(\n payload: ModelChatRequest,\n request: Request,\n session: Annotated[AsyncSession, Depends(get_session)],\n) -> ModelChatResponse:\n \"\"\"处理 chat with model。\n\n Args:\n payload: payload 参数。\n request: request 参数。\n session: session 参数。\n \"\"\"\n return await handle_model_chat(\n payload=payload,\n session=session,\n settings=request.app.state.settings,\n )\n"}, "3d2c50de5bd1d8c85ea260a9868476cba82e716c": {"t": 1785752986.619852, "n": 875, "h": "3d241232a81f1414", "c": "from __future__ import annotations\n\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom infrastructure.database import get_session\nfrom model_gateway.chat_service import handle_model_chat\nfrom app.api.schemas import (\n ModelChatRequest,\n ModelChatResponse,\n)\n\nrouter = APIRouter()\n\n\n@router.post(\"/internal/models/chat\", response_model=ModelChatResponse)\nasync def chat_with_model(\n payload: ModelChatRequest,\n request: Request,\n session: Annotated[AsyncSession, Depends(get_session)],\n) -> ModelChatResponse:\n \"\"\"处理 chat with model。\n\n Args:\n payload: payload 参数。\n request: request 参数。\n session: session 参数。\n \"\"\"\n return await handle_model_chat(\n payload=payload,\n session=session,\n settings=request.app.state.settings,\n )\n"}, "27592d735f25cffd1334f4cecf275f79553d9fdb": {"t": 1785752988.3580093, "n": 742, "h": "a295e17f56156c4c", "c": "from __future__ import annotations\n\nfrom typing import Annotated\n\nfrom fastapi import APIRouter, Depends, Request\nfrom sqlalchemy.ext.asyncio import AsyncSession\n\nfrom infrastructure.database import get_session\nfrom model_gateway.chat_service import handle_model_chat\nfrom app.api.schemas import (\n ModelChatRequest,\n ModelChatResponse,\n)\n\nrouter = APIRouter()\n\n\n@router.post(\"/internal/models/chat\", response_model=ModelChatResponse)\nasync def chat_with_model(\n payload: ModelChatRequest,\n request: Request,\n session: Annotated[AsyncSession, Depends(get_session)],\n) -> ModelChatResponse:\n return await handle_model_chat(\n payload=payload,\n session=session,\n settings=request.app.state.settings,\n )\n"}}}} |