release: bump version to 0.62.0
This commit is contained in:
@@ -45,6 +45,7 @@ def get_provider_service(
|
||||
x_ai_model: str | None = Header(default=None),
|
||||
x_ai_max_tokens: str | None = Header(default=None),
|
||||
x_ai_anthropic_version: str | None = Header(default=None),
|
||||
x_ai_model_provider_apis: str | None = Header(default=None),
|
||||
) -> ProviderService:
|
||||
overrides = {
|
||||
"provider": x_ai_provider,
|
||||
@@ -53,6 +54,7 @@ def get_provider_service(
|
||||
"api_key": x_ai_api_key,
|
||||
"model": x_ai_model,
|
||||
"anthropic_version": x_ai_anthropic_version,
|
||||
"model_provider_apis": x_ai_model_provider_apis,
|
||||
}
|
||||
if x_ai_max_tokens:
|
||||
overrides["max_tokens"] = x_ai_max_tokens
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
@@ -14,7 +15,6 @@ from aiprovider.schemas import (
|
||||
SituationalAnalysisResponse,
|
||||
)
|
||||
|
||||
|
||||
def _normalize_provider(value: str) -> str:
|
||||
return (value or "disabled").strip().lower()
|
||||
|
||||
@@ -62,6 +62,9 @@ class ProviderService:
|
||||
self.anthropic_version = str(
|
||||
overrides.get("anthropic_version") or settings.AI_ANTHROPIC_VERSION
|
||||
)
|
||||
self.model_provider_apis = self._parse_model_provider_apis(
|
||||
overrides.get("model_provider_apis")
|
||||
)
|
||||
|
||||
def get_status(self) -> AIProviderStatusResponse:
|
||||
enabled = self.provider != "disabled"
|
||||
@@ -93,11 +96,13 @@ class ProviderService:
|
||||
|
||||
prompt = self._build_prompt(payload)
|
||||
|
||||
if self.provider_api == "openai-completions":
|
||||
provider_api = self._resolve_model_provider_api(model)
|
||||
|
||||
if provider_api == "openai-completions":
|
||||
data = await self._request_openai_compatible(model, prompt, payload.system_prompt)
|
||||
content = self._extract_openai_content(data)
|
||||
content_blocks = self._extract_openai_blocks(data)
|
||||
elif self.provider_api == "anthropic-messages":
|
||||
elif provider_api == "anthropic-messages":
|
||||
data = await self._request_anthropic_messages(
|
||||
model,
|
||||
prompt,
|
||||
@@ -106,7 +111,7 @@ class ProviderService:
|
||||
)
|
||||
content = self._extract_anthropic_content(data)
|
||||
content_blocks = self._extract_anthropic_blocks(data)
|
||||
elif self.provider_api == "ollama-generate":
|
||||
elif provider_api == "ollama-generate":
|
||||
data = await self._request_ollama(model, prompt, payload.system_prompt)
|
||||
content = self._extract_ollama_content(data)
|
||||
content_blocks = self._extract_ollama_blocks(data)
|
||||
@@ -132,6 +137,26 @@ class ProviderService:
|
||||
def _requires_api_key(self) -> bool:
|
||||
return self.provider_api != "ollama-generate"
|
||||
|
||||
def _resolve_model_provider_api(self, model: str) -> str:
|
||||
return self.model_provider_apis.get(model) or self.provider_api
|
||||
|
||||
def _parse_model_provider_apis(self, value: Any) -> dict[str, str]:
|
||||
if isinstance(value, dict):
|
||||
raw = value
|
||||
elif isinstance(value, str) and value.strip():
|
||||
try:
|
||||
parsed = json.loads(value)
|
||||
except json.JSONDecodeError:
|
||||
return {}
|
||||
raw = parsed if isinstance(parsed, dict) else {}
|
||||
else:
|
||||
raw = {}
|
||||
return {
|
||||
str(model): _normalize_provider_api(str(provider_api))
|
||||
for model, provider_api in raw.items()
|
||||
if model and provider_api
|
||||
}
|
||||
|
||||
def _build_prompt(self, payload: SituationalAnalysisRequest) -> str:
|
||||
sections = [
|
||||
f"任务标题:\n{payload.title}",
|
||||
@@ -314,13 +339,19 @@ class ProviderService:
|
||||
message = choices[0].get("message") or {}
|
||||
content = message.get("content")
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
if content:
|
||||
return content
|
||||
reasoning_content = message.get("reasoning_content")
|
||||
return reasoning_content if isinstance(reasoning_content, str) else ""
|
||||
if isinstance(content, list):
|
||||
return "".join(
|
||||
item.get("text", "")
|
||||
for item in content
|
||||
if isinstance(item, dict)
|
||||
)
|
||||
reasoning_content = message.get("reasoning_content")
|
||||
if isinstance(reasoning_content, str):
|
||||
return reasoning_content
|
||||
return ""
|
||||
|
||||
def _extract_openai_blocks(self, payload: dict[str, Any]) -> list[AIContentBlock]:
|
||||
@@ -331,9 +362,14 @@ class ProviderService:
|
||||
message = choices[0].get("message") or {}
|
||||
content = message.get("content")
|
||||
if isinstance(content, str):
|
||||
return [AIContentBlock(type="text", text=content)]
|
||||
blocks = [AIContentBlock(type="text", text=content)] if content else []
|
||||
reasoning_content = message.get("reasoning_content")
|
||||
if isinstance(reasoning_content, str) and reasoning_content:
|
||||
blocks.append(AIContentBlock(type="thinking", thinking=reasoning_content))
|
||||
return blocks
|
||||
if not isinstance(content, list):
|
||||
return []
|
||||
reasoning_content = message.get("reasoning_content")
|
||||
return [AIContentBlock(type="thinking", thinking=reasoning_content)] if isinstance(reasoning_content, str) and reasoning_content else []
|
||||
|
||||
blocks: list[AIContentBlock] = []
|
||||
for item in content:
|
||||
@@ -346,7 +382,11 @@ class ProviderService:
|
||||
metadata={k: v for k, v in item.items() if k not in {"type", "text"}},
|
||||
)
|
||||
)
|
||||
reasoning_content = message.get("reasoning_content")
|
||||
if isinstance(reasoning_content, str) and reasoning_content:
|
||||
blocks.append(AIContentBlock(type="thinking", thinking=reasoning_content))
|
||||
return blocks
|
||||
|
||||
def _extract_anthropic_content(self, payload: dict[str, Any]) -> str:
|
||||
content = payload.get("content")
|
||||
if isinstance(content, str):
|
||||
|
||||
Reference in New Issue
Block a user