添加provider工厂和Ollama支持
- 实现ProviderFactory用于构建不同类型的provider适配器 - 添加EnvironmentCredentialResolver用于解析环境变量中的凭证 - 实现OllamaProvider支持本地模型调用 - 实现OpenAICompatibleProvider支持OpenAI兼容接口 - 在AgentRuntime中添加对ProviderError的处理 - 更新Message结构体添加tool_calls字段 - 实现provider配置的增删改查API端点 - 添加provider注册表的replace方法 - 添加HTTP基础类和工具参数解码功能 - 更新依赖添加httpx库 - 添加相关单元测试验证provider适配器功能 ```
This commit is contained in:
+63
-6
@@ -1,5 +1,6 @@
|
||||
from collections.abc import AsyncIterator
|
||||
from datetime import datetime, timezone
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, Query
|
||||
from fastapi.responses import StreamingResponse
|
||||
@@ -49,6 +50,7 @@ from app.agent import AgentRunNotFoundError
|
||||
from app.container import container
|
||||
from app.errors import ApiError, not_implemented
|
||||
from app.providers.registry import ProviderNotFoundError
|
||||
from app.providers.factory import UnsupportedProviderError
|
||||
|
||||
router = APIRouter(prefix="/api")
|
||||
not_implemented_response = {501: {"model": ErrorResponse, "description": "业务服务尚未实现"}}
|
||||
@@ -86,6 +88,18 @@ def agent_run_or_404(run_id: str) -> AgentRun:
|
||||
) from exc
|
||||
|
||||
|
||||
def configurable_provider_or_404(provider_id: str):
|
||||
try:
|
||||
return container.providers.get_any(provider_id)
|
||||
except ProviderNotFoundError as exc:
|
||||
raise ApiError(
|
||||
404,
|
||||
"PROVIDER_NOT_FOUND",
|
||||
f"Provider is not registered: {provider_id}",
|
||||
{"provider_id": provider_id},
|
||||
) from exc
|
||||
|
||||
|
||||
# Notes
|
||||
@router.get("/notes", response_model=NoteListResponse, tags=["Notes"])
|
||||
async def list_notes(
|
||||
@@ -398,7 +412,7 @@ async def list_providers() -> ProviderListResponse:
|
||||
tags=["Providers"],
|
||||
)
|
||||
async def get_provider(provider_id: str) -> ProviderConfig:
|
||||
return provider_or_404(provider_id).config.model_copy(deep=True)
|
||||
return configurable_provider_or_404(provider_id).config.model_copy(deep=True)
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -407,8 +421,27 @@ async def get_provider(provider_id: str) -> ProviderConfig:
|
||||
responses=not_implemented_response,
|
||||
tags=["Providers"],
|
||||
)
|
||||
async def create_provider(_: ProviderCreateRequest) -> ProviderConfig:
|
||||
not_implemented("providers.create")
|
||||
async def create_provider(request: ProviderCreateRequest) -> ProviderConfig:
|
||||
config = ProviderConfig(
|
||||
provider_id=f"provider_{uuid4().hex}",
|
||||
provider_type=request.provider_type,
|
||||
name=request.name,
|
||||
base_url=request.base_url,
|
||||
default_model=request.default_model,
|
||||
credential_id=request.credential_id,
|
||||
enabled=request.enabled,
|
||||
capabilities=container.provider_factory.capabilities(request.provider_type),
|
||||
)
|
||||
try:
|
||||
adapter = container.provider_factory.build(config)
|
||||
except UnsupportedProviderError as exc:
|
||||
raise ApiError(
|
||||
422,
|
||||
"PROVIDER_TYPE_UNSUPPORTED",
|
||||
f"Provider adapter is not implemented: {request.provider_type.value}",
|
||||
) from exc
|
||||
container.providers.register(config, adapter)
|
||||
return config
|
||||
|
||||
|
||||
@router.patch(
|
||||
@@ -417,8 +450,16 @@ async def create_provider(_: ProviderCreateRequest) -> ProviderConfig:
|
||||
responses=not_implemented_response,
|
||||
tags=["Providers"],
|
||||
)
|
||||
async def update_provider(provider_id: str, _: ProviderUpdateRequest) -> ProviderConfig:
|
||||
not_implemented(f"providers.update:{provider_id}")
|
||||
async def update_provider(
|
||||
provider_id: str, request: ProviderUpdateRequest
|
||||
) -> ProviderConfig:
|
||||
current = configurable_provider_or_404(provider_id).config
|
||||
if provider_id == "mock":
|
||||
raise ApiError(409, "BUILTIN_PROVIDER_IMMUTABLE", "Mock provider cannot be modified.")
|
||||
config = current.model_copy(update=request.model_dump(exclude_none=True))
|
||||
adapter = container.provider_factory.build(config)
|
||||
container.providers.replace(config, adapter)
|
||||
return config
|
||||
|
||||
|
||||
@router.delete(
|
||||
@@ -428,7 +469,11 @@ async def update_provider(provider_id: str, _: ProviderUpdateRequest) -> Provide
|
||||
tags=["Providers"],
|
||||
)
|
||||
async def delete_provider(provider_id: str) -> OperationResponse:
|
||||
not_implemented(f"providers.delete:{provider_id}")
|
||||
configurable_provider_or_404(provider_id)
|
||||
if provider_id == "mock":
|
||||
raise ApiError(409, "BUILTIN_PROVIDER_IMMUTABLE", "Mock provider cannot be deleted.")
|
||||
container.providers.unregister(provider_id)
|
||||
return OperationResponse(status="completed", resource_id=provider_id)
|
||||
|
||||
|
||||
@router.get(
|
||||
@@ -452,6 +497,18 @@ async def list_provider_models(provider_id: str) -> ProviderModelsResponse:
|
||||
tags=["Providers"],
|
||||
)
|
||||
async def test_provider(request: ProviderTestRequest) -> ProviderTestResponse:
|
||||
registered = configurable_provider_or_404(request.provider_id)
|
||||
if request.credential_context_id:
|
||||
temporary_config = registered.config.model_copy(
|
||||
update={"credential_id": request.credential_context_id, "enabled": True}
|
||||
)
|
||||
adapter = container.provider_factory.build(temporary_config)
|
||||
success, message = await adapter.test_connection(request.model)
|
||||
return ProviderTestResponse(
|
||||
provider_id=request.provider_id,
|
||||
success=success,
|
||||
message=message,
|
||||
)
|
||||
provider_or_404(request.provider_id)
|
||||
return await container.providers.test(request.provider_id, request.model)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user