88c1ea297e
All feature containers now POST messages to ai-service (port 8010) instead of calling AI providers directly. ai-service routes to LM Studio, Ollama, or Anthropic based on /config/ai_service_config.json. doc-service AI providers removed; replaced by httpx ai_client.py. Backend settings restructured to /api/settings/ai. Frontend gets dedicated AIAdminSettingsPage and AI Service card in AppsPage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
53 lines
1.8 KiB
Python
53 lines
1.8 KiB
Python
"""OpenAI-compatible provider — handles both Ollama and LM Studio."""
|
|
import asyncio
|
|
|
|
import openai
|
|
|
|
from app.providers.base import AIProvider
|
|
from app.schemas.chat import ChatMessage
|
|
|
|
|
|
class OpenAICompatProvider(AIProvider):
|
|
def __init__(self, config: dict, provider_name: str = "lmstudio") -> None:
|
|
self._client = openai.AsyncOpenAI(
|
|
base_url=config.get("base_url", "http://localhost:1234/v1"),
|
|
api_key=config.get("api_key") or "not-required",
|
|
)
|
|
self.model_name = config.get("model", "local-model")
|
|
self.provider_name = provider_name
|
|
|
|
async def chat(
|
|
self,
|
|
messages: list[ChatMessage],
|
|
max_tokens: int,
|
|
temperature: float,
|
|
) -> tuple[str, int | None, int | None]:
|
|
raw_messages = [{"role": m.role, "content": m.content} for m in messages]
|
|
try:
|
|
response = await self._client.chat.completions.create(
|
|
model=self.model_name,
|
|
messages=raw_messages,
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
)
|
|
except openai.APIConnectionError as exc:
|
|
raise ProviderConnectionError(str(exc)) from exc
|
|
except openai.APITimeoutError as exc:
|
|
raise ProviderTimeoutError(str(exc)) from exc
|
|
except openai.APIStatusError as exc:
|
|
raise ProviderConnectionError(f"API error {exc.status_code}: {exc.message}") from exc
|
|
|
|
content = response.choices[0].message.content or ""
|
|
usage = response.usage
|
|
input_tokens = usage.prompt_tokens if usage else None
|
|
output_tokens = usage.completion_tokens if usage else None
|
|
return content, input_tokens, output_tokens
|
|
|
|
|
|
class ProviderConnectionError(Exception):
|
|
pass
|
|
|
|
|
|
class ProviderTimeoutError(Exception):
|
|
pass
|