Coverage for src/chat_limiter/types.py: 93%
60 statements
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-11 12:02 +0100
« prev ^ index » next coverage.py v7.9.2, created at 2025-07-11 12:02 +0100
1"""
2Type definitions for chat completion requests and responses.
3"""
5from dataclasses import dataclass
6from enum import Enum
7from typing import Any
9from pydantic import BaseModel
12class MessageRole(str, Enum):
13 """Message roles supported across providers."""
15 USER = "user"
16 ASSISTANT = "assistant"
17 SYSTEM = "system"
20@dataclass
21class Message:
22 """A chat message that works across all providers."""
24 role: MessageRole
25 content: str
28class ChatCompletionRequest(BaseModel):
29 """High-level chat completion request."""
31 model: str
32 messages: list[Message]
33 max_tokens: int | None = None
34 temperature: float | None = None
35 top_p: float | None = None
36 stop: str | list[str] | None = None
37 stream: bool = False
39 # Provider-specific parameters (will be filtered per provider)
40 frequency_penalty: float | None = None # OpenAI
41 presence_penalty: float | None = None # OpenAI
42 top_k: int | None = None # Anthropic
45@dataclass
46class Usage:
47 """Token usage information."""
49 prompt_tokens: int
50 completion_tokens: int
51 total_tokens: int
54@dataclass
55class Choice:
56 """A completion choice."""
58 index: int
59 message: Message
60 finish_reason: str | None = None
63@dataclass
64class ChatCompletionResponse:
65 """High-level chat completion response."""
67 id: str
68 model: str
69 choices: list[Choice]
70 usage: Usage | None = None
71 created: int | None = None
73 # Provider-specific metadata
74 provider: str | None = None
75 raw_response: dict[str, Any] | None = None
78# Model mappings for each provider
79OPENAI_MODELS = {
80 "gpt-4o",
81 "gpt-4o-mini",
82 "gpt-4-turbo",
83 "gpt-4",
84 "gpt-3.5-turbo",
85 "gpt-3.5-turbo-16k",
86}
88ANTHROPIC_MODELS = {
89 "claude-3-5-sonnet-20241022",
90 "claude-3-5-haiku-20241022",
91 "claude-3-opus-20240229",
92 "claude-3-sonnet-20240229",
93 "claude-3-haiku-20240307",
94}
96OPENROUTER_MODELS = {
97 # OpenAI models via OpenRouter
98 "openai/gpt-4o",
99 "openai/gpt-4o-mini",
100 "openai/gpt-4-turbo",
101 "openai/gpt-3.5-turbo",
103 # Anthropic models via OpenRouter
104 "anthropic/claude-3-5-sonnet",
105 "anthropic/claude-3-opus",
106 "anthropic/claude-3-sonnet",
107 "anthropic/claude-3-haiku",
109 # Other providers via OpenRouter
110 "meta-llama/llama-3.1-405b-instruct",
111 "meta-llama/llama-3.1-70b-instruct",
112 "google/gemini-pro",
113 "cohere/command-r-plus",
114}
116ALL_MODELS = OPENAI_MODELS | ANTHROPIC_MODELS | OPENROUTER_MODELS
119def detect_provider_from_model(model: str, use_dynamic_discovery: bool = False, api_keys: dict[str, str] | None = None) -> str | None:
120 """
121 Detect provider from model name.
123 Args:
124 model: The model name to check
125 use_dynamic_discovery: Whether to use live API queries for model discovery
126 api_keys: Dictionary of API keys for dynamic discovery
128 Returns:
129 Provider name or None if not found
130 """
131 # First try pattern-based detection for common cases
132 if "/" in model: # OpenRouter format
133 return "openrouter"
135 # Check hardcoded lists for fast lookup
136 if model in OPENAI_MODELS:
137 return "openai"
138 elif model in ANTHROPIC_MODELS:
139 return "anthropic"
140 elif model in OPENROUTER_MODELS:
141 return "openrouter"
143 # If dynamic discovery is enabled and we have API keys, try that
144 if use_dynamic_discovery and api_keys:
145 from .models import detect_provider_from_model_sync
146 result = detect_provider_from_model_sync(model, api_keys)
147 return result.found_provider
149 return None