Diomedes Git commited on
Commit
d5af2c0
·
1 Parent(s): de7bf4b

refactoring characters to use fallback inference to protect against rate limits, etc

Browse files
pyproject.toml CHANGED
@@ -15,6 +15,7 @@ dependencies = [
15
  "mcp>=1.20.0",
16
  "newsapi>=0.1.1",
17
  "newsapi-python>=0.2.7",
 
18
  "openaq>=0.5.0",
19
  "python-dotenv>=1.2.1",
20
  "pytrends>=4.9.2",
 
15
  "mcp>=1.20.0",
16
  "newsapi>=0.1.1",
17
  "newsapi-python>=0.2.7",
18
+ "openai>=2.8.1",
19
  "openaq>=0.5.0",
20
  "python-dotenv>=1.2.1",
21
  "pytrends>=4.9.2",
src/characters/check_char_init.py CHANGED
@@ -27,29 +27,51 @@ characters = {
27
  print("--- Initializing Characters ---")
28
 
29
  # Set a dummy GROQ_API_KEY for testing purposes if it's not already set
30
- # This allows the use_groq=True path to be tested without an actual key,
31
- # but it will still raise an error if the key is explicitly checked for existence
32
- # in the character's __init__ method (which is good, as it tests that check).
33
  if "GROQ_API_KEY" not in os.environ:
34
  os.environ["GROQ_API_KEY"] = "dummy_key_for_testing"
35
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  for name, char_class in characters.items():
37
  print(f"Testing {name}...")
38
 
39
- # Test with use_groq=True
40
  try:
41
- instance = char_class(use_groq=True)
42
- print(f" ✅ {name} (use_groq=True) initialized successfully.")
43
  except ValueError as e:
44
- print(f" ❌ {name} (use_groq=True) failed to initialize: {e} (Expected if GROQ_API_KEY is missing or invalid)")
45
  except Exception as e:
46
- print(f" ❌ {name} (use_groq=True) failed to initialize with unexpected error: {e}")
47
 
48
- # Test with use_groq=False (Ollama path)
49
  try:
50
- instance = char_class(use_groq=False)
51
- print(f" ✅ {name} (use_groq=False) initialized successfully.")
 
 
 
 
 
 
 
 
 
52
  except Exception as e:
53
- print(f" ❌ {name} (use_groq=False) failed to initialize: {e}")
54
 
55
  print("--- Character Initialization Test Complete ---")
 
27
  print("--- Initializing Characters ---")
28
 
29
  # Set a dummy GROQ_API_KEY for testing purposes if it's not already set
 
 
 
30
  if "GROQ_API_KEY" not in os.environ:
31
  os.environ["GROQ_API_KEY"] = "dummy_key_for_testing"
32
 
33
+ # Define test configurations
34
+ cloud_config = {
35
+ "primary": "groq",
36
+ "fallback": ["nebius"],
37
+ "models": {
38
+ "groq": "llama-3.1-70b-versatile",
39
+ "nebius": "meta-llama/Meta-Llama-3.1-70B-Instruct"
40
+ },
41
+ "timeout": 30,
42
+ "use_cloud": True
43
+ }
44
+
45
+ ollama_config = {
46
+ "use_cloud": False
47
+ }
48
+
49
  for name, char_class in characters.items():
50
  print(f"Testing {name}...")
51
 
52
+ # Test with cloud providers (use_cloud=True)
53
  try:
54
+ instance = char_class(provider_config=cloud_config)
55
+ print(f" ✅ {name} (cloud providers) initialized successfully.")
56
  except ValueError as e:
57
+ print(f" ❌ {name} (cloud providers) failed to initialize: {e} (Expected if API keys are missing)")
58
  except Exception as e:
59
+ print(f" ❌ {name} (cloud providers) failed with unexpected error: {e}")
60
 
61
+ # Test with Ollama (use_cloud=False)
62
  try:
63
+ instance = char_class(provider_config=ollama_config)
64
+ print(f" ✅ {name} (Ollama) initialized successfully.")
65
+ except Exception as e:
66
+ print(f" ❌ {name} (Ollama) failed to initialize: {e}")
67
+
68
+ # Test with default config (None - should use defaults)
69
+ try:
70
+ instance = char_class()
71
+ print(f" ✅ {name} (default config) initialized successfully.")
72
+ except ValueError as e:
73
+ print(f" ⚠️ {name} (default config) failed: {e} (Expected if no API keys configured)")
74
  except Exception as e:
75
+ print(f" ❌ {name} (default config) failed with unexpected error: {e}")
76
 
77
  print("--- Character Initialization Test Complete ---")
src/characters/raven.py CHANGED
@@ -1,9 +1,11 @@
1
  import os
2
  import json
3
  import asyncio
 
4
  from typing import Optional, List, Dict, Any
5
  from dotenv import load_dotenv
6
  from groq import Groq
 
7
  from src.cluas_mcp.news.news_search import search_news
8
  from src.cluas_mcp.web.web_search import search_web
9
  from src.cluas_mcp.web.trending import fetch_trends
@@ -11,12 +13,13 @@ from src.cluas_mcp.common.paper_memory import PaperMemory
11
  from src.cluas_mcp.common.observation_memory import ObservationMemory
12
 
13
  load_dotenv()
 
 
14
 
15
  class Raven:
16
- def __init__(self, use_groq=True, location="Seattle, WA"):
17
  self.name = "Raven"
18
  self.location = location
19
- self.use_groq = use_groq
20
  self.tools = ["search_news", "search_web", "fetch_trends"]
21
  self.paper_memory = PaperMemory()
22
  self.observation_memory = ObservationMemory(location=location)
@@ -26,14 +29,52 @@ class Raven:
26
  "fetch_trends": fetch_trends,
27
  }
28
 
29
- if use_groq:
30
- api_key = os.getenv("GROQ_API_KEY")
31
- if not api_key:
32
- raise ValueError("GROQ_API_KEY not found in environment")
33
- self.client = Groq(api_key=api_key)
34
- self.model = "openai/gpt-oss-120b"
 
 
 
 
 
 
 
 
 
 
 
 
35
  else:
 
36
  self.model = "llama3.1:8b"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  def get_system_prompt(self) -> str:
39
  return """You are Raven, a passionate activist and truth-seeker.
@@ -59,24 +100,9 @@ TOOLS AVAILABLE:
59
 
60
  When you need to verify information or find current news, use your tools!"""
61
 
62
- async def respond(self,
63
- message: str,
64
- conversation_history: Optional[List[Dict]] = None) -> str:
65
- """Generate a response."""
66
- if self.use_groq:
67
- return await self._respond_groq(message, conversation_history)
68
- return self._respond_ollama(message, conversation_history)
69
-
70
- async def _respond_groq(self, message: str, history: Optional[List[Dict]] = None) -> str:
71
- """Use Groq with tool calling for Raven's investigative workflow."""
72
- messages = [{"role": "system", "content": self.get_system_prompt()}]
73
-
74
- if history:
75
- messages.extend(history[-5:])
76
-
77
- messages.append({"role": "user", "content": message})
78
-
79
- tools = [
80
  {
81
  "type": "function",
82
  "function": {
@@ -134,17 +160,72 @@ When you need to verify information or find current news, use your tools!"""
134
  }
135
  ]
136
 
137
- first_response = self.client.chat.completions.create(
138
- model=self.model,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  messages=messages,
140
  tools=tools,
141
- tool_choice="auto",
142
  temperature=0.8,
143
  max_tokens=150
144
  )
145
 
146
  choice = first_response.choices[0]
147
 
 
148
  if choice.finish_reason == "tool_calls" and choice.message.tool_calls:
149
  tool_call = choice.message.tool_calls[0]
150
  tool_name = tool_call.function.name
@@ -157,6 +238,7 @@ When you need to verify information or find current news, use your tools!"""
157
 
158
  formatted = self._format_tool_result(tool_name, tool_result)
159
 
 
160
  messages.append({
161
  "role": "assistant",
162
  "content": None,
@@ -175,8 +257,8 @@ When you need to verify information or find current news, use your tools!"""
175
  "content": formatted
176
  })
177
 
178
- second_response = self.client.chat.completions.create(
179
- model=self.model,
180
  messages=messages,
181
  temperature=0.8,
182
  max_tokens=200
@@ -237,7 +319,6 @@ When you need to verify information or find current news, use your tools!"""
237
  """Placeholder for local inference without tool calls."""
238
  prompt = self._build_prompt(message, history)
239
 
240
- # requests import has to be at the top of the file
241
  import requests
242
 
243
  response = requests.post('http://localhost:11434/api/generate', json={
@@ -271,5 +352,4 @@ When you need to verify information or find current news, use your tools!"""
271
  transcript.append(f"Raven: {content}")
272
  transcript.append(f"User: {message}")
273
  transcript.append("Raven:")
274
- return "\n\n".join(transcript)
275
-
 
1
  import os
2
  import json
3
  import asyncio
4
+ import logging
5
  from typing import Optional, List, Dict, Any
6
  from dotenv import load_dotenv
7
  from groq import Groq
8
+ from openai import OpenAI
9
  from src.cluas_mcp.news.news_search import search_news
10
  from src.cluas_mcp.web.web_search import search_web
11
  from src.cluas_mcp.web.trending import fetch_trends
 
13
  from src.cluas_mcp.common.observation_memory import ObservationMemory
14
 
15
  load_dotenv()
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
 
19
  class Raven:
20
+ def __init__(self, provider_config=None, location="Seattle, WA"):
21
  self.name = "Raven"
22
  self.location = location
 
23
  self.tools = ["search_news", "search_web", "fetch_trends"]
24
  self.paper_memory = PaperMemory()
25
  self.observation_memory = ObservationMemory(location=location)
 
29
  "fetch_trends": fetch_trends,
30
  }
31
 
32
+ # Default provider priority
33
+ if provider_config is None:
34
+ provider_config = {
35
+ "primary": "groq",
36
+ "fallback": ["nebius"],
37
+ "models": {
38
+ "groq": "llama-3.1-70b-versatile",
39
+ "nebius": "meta-llama/Meta-Llama-3.1-70B-Instruct"
40
+ },
41
+ "timeout": 30,
42
+ "use_cloud": True
43
+ }
44
+
45
+ self.provider_config = provider_config
46
+ self.use_cloud = provider_config.get("use_cloud", True)
47
+
48
+ if self.use_cloud:
49
+ self._init_clients()
50
  else:
51
+ # Local ollama fallback
52
  self.model = "llama3.1:8b"
53
+
54
+ def _init_clients(self):
55
+ """Initialize all available provider clients"""
56
+ self.clients = {}
57
+
58
+ # Groq
59
+ if os.getenv("GROQ_API_KEY"):
60
+ self.clients["groq"] = Groq(
61
+ api_key=os.getenv("GROQ_API_KEY"),
62
+ timeout=self.provider_config.get("timeout", 30)
63
+ )
64
+
65
+ # Nebius Token Factory (OpenAI-compatible)
66
+ if os.getenv("NEBIUS_API_KEY"):
67
+ self.clients["nebius"] = OpenAI(
68
+ api_key=os.getenv("NEBIUS_API_KEY"),
69
+ base_url="https://api.tokenfactory.nebius.com/v1",
70
+ timeout=self.provider_config.get("timeout", 30)
71
+ )
72
+
73
+ # Log which providers are available
74
+ available = list(self.clients.keys())
75
+ if not available:
76
+ raise ValueError(f"{self.name}: No LLM provider API keys found in environment")
77
+ logger.info(f"{self.name} initialized with providers: {available}")
78
 
79
  def get_system_prompt(self) -> str:
80
  return """You are Raven, a passionate activist and truth-seeker.
 
100
 
101
  When you need to verify information or find current news, use your tools!"""
102
 
103
+ def _get_tool_definitions(self) -> List[Dict]:
104
+ """Return tool definitions for function calling"""
105
+ return [
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  {
107
  "type": "function",
108
  "function": {
 
160
  }
161
  ]
162
 
163
+ def _call_llm(self, messages: List[Dict], tools: Optional[List[Dict]] = None,
164
+ temperature: float = 0.8, max_tokens: int = 150) -> tuple:
165
+ """Call LLM with automatic fallback"""
166
+ providers = [self.provider_config["primary"]] + self.provider_config["fallback"]
167
+ last_error = None
168
+
169
+ for provider in providers:
170
+ if provider not in self.clients:
171
+ logger.debug(f"{self.name}: Skipping {provider} - not configured")
172
+ continue
173
+
174
+ try:
175
+ model = self.provider_config["models"][provider]
176
+ logger.debug(f"{self.name} trying {provider} with model {model}")
177
+
178
+ # Both Groq and OpenAI clients have same interface
179
+ response = self.clients[provider].chat.completions.create(
180
+ model=model,
181
+ messages=messages,
182
+ tools=tools,
183
+ tool_choice="auto" if tools else None,
184
+ temperature=temperature,
185
+ max_tokens=max_tokens
186
+ )
187
+
188
+ logger.info(f"{self.name} successfully used {provider}")
189
+ return response, provider
190
+
191
+ except Exception as e:
192
+ last_error = e
193
+ logger.warning(f"{self.name}: {provider} failed - {str(e)[:100]}")
194
+ continue
195
+
196
+ # If we get here, all providers failed
197
+ raise Exception(f"All LLM providers failed for {self.name}. Last error: {last_error}")
198
+
199
+ async def respond(self,
200
+ message: str,
201
+ conversation_history: Optional[List[Dict]] = None) -> str:
202
+ """Generate a response."""
203
+ if self.use_cloud:
204
+ return await self._respond_cloud(message, conversation_history)
205
+ return self._respond_ollama(message, conversation_history)
206
+
207
+ async def _respond_cloud(self, message: str, history: Optional[List[Dict]] = None) -> str:
208
+ """Use cloud providers with tool calling for Raven's investigative workflow."""
209
+ messages = [{"role": "system", "content": self.get_system_prompt()}]
210
+
211
+ if history:
212
+ messages.extend(history[-5:])
213
+
214
+ messages.append({"role": "user", "content": message})
215
+
216
+ tools = self._get_tool_definitions()
217
+
218
+ # First LLM call - may trigger tool use
219
+ first_response, provider = self._call_llm(
220
  messages=messages,
221
  tools=tools,
 
222
  temperature=0.8,
223
  max_tokens=150
224
  )
225
 
226
  choice = first_response.choices[0]
227
 
228
+ # Check if LLM wants to use tools
229
  if choice.finish_reason == "tool_calls" and choice.message.tool_calls:
230
  tool_call = choice.message.tool_calls[0]
231
  tool_name = tool_call.function.name
 
238
 
239
  formatted = self._format_tool_result(tool_name, tool_result)
240
 
241
+ # Add tool call and result to messages
242
  messages.append({
243
  "role": "assistant",
244
  "content": None,
 
257
  "content": formatted
258
  })
259
 
260
+ # Second LLM call with tool results
261
+ second_response, _ = self._call_llm(
262
  messages=messages,
263
  temperature=0.8,
264
  max_tokens=200
 
319
  """Placeholder for local inference without tool calls."""
320
  prompt = self._build_prompt(message, history)
321
 
 
322
  import requests
323
 
324
  response = requests.post('http://localhost:11434/api/generate', json={
 
352
  transcript.append(f"Raven: {content}")
353
  transcript.append(f"User: {message}")
354
  transcript.append("Raven:")
355
+ return "\n\n".join(transcript)
 
uv.lock CHANGED
@@ -342,6 +342,7 @@ dependencies = [
342
  { name = "mcp" },
343
  { name = "newsapi" },
344
  { name = "newsapi-python" },
 
345
  { name = "openaq" },
346
  { name = "python-dotenv" },
347
  { name = "pytrends" },
@@ -367,6 +368,7 @@ requires-dist = [
367
  { name = "mcp", specifier = ">=1.20.0" },
368
  { name = "newsapi", specifier = ">=0.1.1" },
369
  { name = "newsapi-python", specifier = ">=0.2.7" },
 
370
  { name = "openaq", specifier = ">=0.5.0" },
371
  { name = "python-dotenv", specifier = ">=1.2.1" },
372
  { name = "pytrends", specifier = ">=4.9.2" },
@@ -889,6 +891,74 @@ wheels = [
889
  { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
890
  ]
891
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
892
  [[package]]
893
  name = "jsonschema"
894
  version = "4.25.1"
@@ -1233,6 +1303,25 @@ wheels = [
1233
  { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
1234
  ]
1235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1236
  [[package]]
1237
  name = "openapi-pydantic"
1238
  version = "0.5.1"
 
342
  { name = "mcp" },
343
  { name = "newsapi" },
344
  { name = "newsapi-python" },
345
+ { name = "openai" },
346
  { name = "openaq" },
347
  { name = "python-dotenv" },
348
  { name = "pytrends" },
 
368
  { name = "mcp", specifier = ">=1.20.0" },
369
  { name = "newsapi", specifier = ">=0.1.1" },
370
  { name = "newsapi-python", specifier = ">=0.2.7" },
371
+ { name = "openai", specifier = ">=2.8.1" },
372
  { name = "openaq", specifier = ">=0.5.0" },
373
  { name = "python-dotenv", specifier = ">=1.2.1" },
374
  { name = "pytrends", specifier = ">=4.9.2" },
 
891
  { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
892
  ]
893
 
894
+ [[package]]
895
+ name = "jiter"
896
+ version = "0.12.0"
897
+ source = { registry = "https://pypi.org/simple" }
898
+ sdist = { url = "https://files.pythonhosted.org/packages/45/9d/e0660989c1370e25848bb4c52d061c71837239738ad937e83edca174c273/jiter-0.12.0.tar.gz", hash = "sha256:64dfcd7d5c168b38d3f9f8bba7fc639edb3418abcc74f22fdbe6b8938293f30b", size = 168294, upload-time = "2025-11-09T20:49:23.302Z" }
899
+ wheels = [
900
+ { url = "https://files.pythonhosted.org/packages/92/c9/5b9f7b4983f1b542c64e84165075335e8a236fa9e2ea03a0c79780062be8/jiter-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:305e061fa82f4680607a775b2e8e0bcb071cd2205ac38e6ef48c8dd5ebe1cf37", size = 314449, upload-time = "2025-11-09T20:47:22.999Z" },
901
+ { url = "https://files.pythonhosted.org/packages/98/6e/e8efa0e78de00db0aee82c0cf9e8b3f2027efd7f8a71f859d8f4be8e98ef/jiter-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1860627048e302a528333c9307c818c547f214d8659b0705d2195e1a94b274", size = 319855, upload-time = "2025-11-09T20:47:24.779Z" },
902
+ { url = "https://files.pythonhosted.org/packages/20/26/894cd88e60b5d58af53bec5c6759d1292bd0b37a8b5f60f07abf7a63ae5f/jiter-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df37577a4f8408f7e0ec3205d2a8f87672af8f17008358063a4d6425b6081ce3", size = 350171, upload-time = "2025-11-09T20:47:26.469Z" },
903
+ { url = "https://files.pythonhosted.org/packages/f5/27/a7b818b9979ac31b3763d25f3653ec3a954044d5e9f5d87f2f247d679fd1/jiter-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fdd787356c1c13a4f40b43c2156276ef7a71eb487d98472476476d803fb2cf", size = 365590, upload-time = "2025-11-09T20:47:27.918Z" },
904
+ { url = "https://files.pythonhosted.org/packages/ba/7e/e46195801a97673a83746170b17984aa8ac4a455746354516d02ca5541b4/jiter-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1eb5db8d9c65b112aacf14fcd0faae9913d07a8afea5ed06ccdd12b724e966a1", size = 479462, upload-time = "2025-11-09T20:47:29.654Z" },
905
+ { url = "https://files.pythonhosted.org/packages/ca/75/f833bfb009ab4bd11b1c9406d333e3b4357709ed0570bb48c7c06d78c7dd/jiter-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73c568cc27c473f82480abc15d1301adf333a7ea4f2e813d6a2c7d8b6ba8d0df", size = 378983, upload-time = "2025-11-09T20:47:31.026Z" },
906
+ { url = "https://files.pythonhosted.org/packages/71/b3/7a69d77943cc837d30165643db753471aff5df39692d598da880a6e51c24/jiter-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4321e8a3d868919bcb1abb1db550d41f2b5b326f72df29e53b2df8b006eb9403", size = 361328, upload-time = "2025-11-09T20:47:33.286Z" },
907
+ { url = "https://files.pythonhosted.org/packages/b0/ac/a78f90caf48d65ba70d8c6efc6f23150bc39dc3389d65bbec2a95c7bc628/jiter-0.12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a51bad79f8cc9cac2b4b705039f814049142e0050f30d91695a2d9a6611f126", size = 386740, upload-time = "2025-11-09T20:47:34.703Z" },
908
+ { url = "https://files.pythonhosted.org/packages/39/b6/5d31c2cc8e1b6a6bcf3c5721e4ca0a3633d1ab4754b09bc7084f6c4f5327/jiter-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a67b678f6a5f1dd6c36d642d7db83e456bc8b104788262aaefc11a22339f5a9", size = 520875, upload-time = "2025-11-09T20:47:36.058Z" },
909
+ { url = "https://files.pythonhosted.org/packages/30/b5/4df540fae4e9f68c54b8dab004bd8c943a752f0b00efd6e7d64aa3850339/jiter-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efe1a211fe1fd14762adea941e3cfd6c611a136e28da6c39272dbb7a1bbe6a86", size = 511457, upload-time = "2025-11-09T20:47:37.932Z" },
910
+ { url = "https://files.pythonhosted.org/packages/07/65/86b74010e450a1a77b2c1aabb91d4a91dd3cd5afce99f34d75fd1ac64b19/jiter-0.12.0-cp312-cp312-win32.whl", hash = "sha256:d779d97c834b4278276ec703dc3fc1735fca50af63eb7262f05bdb4e62203d44", size = 204546, upload-time = "2025-11-09T20:47:40.47Z" },
911
+ { url = "https://files.pythonhosted.org/packages/1c/c7/6659f537f9562d963488e3e55573498a442503ced01f7e169e96a6110383/jiter-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e8269062060212b373316fe69236096aaf4c49022d267c6736eebd66bbbc60bb", size = 205196, upload-time = "2025-11-09T20:47:41.794Z" },
912
+ { url = "https://files.pythonhosted.org/packages/21/f4/935304f5169edadfec7f9c01eacbce4c90bb9a82035ac1de1f3bd2d40be6/jiter-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:06cb970936c65de926d648af0ed3d21857f026b1cf5525cb2947aa5e01e05789", size = 186100, upload-time = "2025-11-09T20:47:43.007Z" },
913
+ { url = "https://files.pythonhosted.org/packages/3d/a6/97209693b177716e22576ee1161674d1d58029eb178e01866a0422b69224/jiter-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6cc49d5130a14b732e0612bc76ae8db3b49898732223ef8b7599aa8d9810683e", size = 313658, upload-time = "2025-11-09T20:47:44.424Z" },
914
+ { url = "https://files.pythonhosted.org/packages/06/4d/125c5c1537c7d8ee73ad3d530a442d6c619714b95027143f1b61c0b4dfe0/jiter-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:37f27a32ce36364d2fa4f7fdc507279db604d27d239ea2e044c8f148410defe1", size = 318605, upload-time = "2025-11-09T20:47:45.973Z" },
915
+ { url = "https://files.pythonhosted.org/packages/99/bf/a840b89847885064c41a5f52de6e312e91fa84a520848ee56c97e4fa0205/jiter-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbc0944aa3d4b4773e348cda635252824a78f4ba44328e042ef1ff3f6080d1cf", size = 349803, upload-time = "2025-11-09T20:47:47.535Z" },
916
+ { url = "https://files.pythonhosted.org/packages/8a/88/e63441c28e0db50e305ae23e19c1d8fae012d78ed55365da392c1f34b09c/jiter-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da25c62d4ee1ffbacb97fac6dfe4dcd6759ebdc9015991e92a6eae5816287f44", size = 365120, upload-time = "2025-11-09T20:47:49.284Z" },
917
+ { url = "https://files.pythonhosted.org/packages/0a/7c/49b02714af4343970eb8aca63396bc1c82fa01197dbb1e9b0d274b550d4e/jiter-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:048485c654b838140b007390b8182ba9774621103bd4d77c9c3f6f117474ba45", size = 479918, upload-time = "2025-11-09T20:47:50.807Z" },
918
+ { url = "https://files.pythonhosted.org/packages/69/ba/0a809817fdd5a1db80490b9150645f3aae16afad166960bcd562be194f3b/jiter-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:635e737fbb7315bef0037c19b88b799143d2d7d3507e61a76751025226b3ac87", size = 379008, upload-time = "2025-11-09T20:47:52.211Z" },
919
+ { url = "https://files.pythonhosted.org/packages/5f/c3/c9fc0232e736c8877d9e6d83d6eeb0ba4e90c6c073835cc2e8f73fdeef51/jiter-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e017c417b1ebda911bd13b1e40612704b1f5420e30695112efdbed8a4b389ed", size = 361785, upload-time = "2025-11-09T20:47:53.512Z" },
920
+ { url = "https://files.pythonhosted.org/packages/96/61/61f69b7e442e97ca6cd53086ddc1cf59fb830549bc72c0a293713a60c525/jiter-0.12.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:89b0bfb8b2bf2351fba36bb211ef8bfceba73ef58e7f0c68fb67b5a2795ca2f9", size = 386108, upload-time = "2025-11-09T20:47:54.893Z" },
921
+ { url = "https://files.pythonhosted.org/packages/e9/2e/76bb3332f28550c8f1eba3bf6e5efe211efda0ddbbaf24976bc7078d42a5/jiter-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f5aa5427a629a824a543672778c9ce0c5e556550d1569bb6ea28a85015287626", size = 519937, upload-time = "2025-11-09T20:47:56.253Z" },
922
+ { url = "https://files.pythonhosted.org/packages/84/d6/fa96efa87dc8bff2094fb947f51f66368fa56d8d4fc9e77b25d7fbb23375/jiter-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed53b3d6acbcb0fd0b90f20c7cb3b24c357fe82a3518934d4edfa8c6898e498c", size = 510853, upload-time = "2025-11-09T20:47:58.32Z" },
923
+ { url = "https://files.pythonhosted.org/packages/8a/28/93f67fdb4d5904a708119a6ab58a8f1ec226ff10a94a282e0215402a8462/jiter-0.12.0-cp313-cp313-win32.whl", hash = "sha256:4747de73d6b8c78f2e253a2787930f4fffc68da7fa319739f57437f95963c4de", size = 204699, upload-time = "2025-11-09T20:47:59.686Z" },
924
+ { url = "https://files.pythonhosted.org/packages/c4/1f/30b0eb087045a0abe2a5c9c0c0c8da110875a1d3be83afd4a9a4e548be3c/jiter-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:e25012eb0c456fcc13354255d0338cd5397cce26c77b2832b3c4e2e255ea5d9a", size = 204258, upload-time = "2025-11-09T20:48:01.01Z" },
925
+ { url = "https://files.pythonhosted.org/packages/2c/f4/2b4daf99b96bce6fc47971890b14b2a36aef88d7beb9f057fafa032c6141/jiter-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:c97b92c54fe6110138c872add030a1f99aea2401ddcdaa21edf74705a646dd60", size = 185503, upload-time = "2025-11-09T20:48:02.35Z" },
926
+ { url = "https://files.pythonhosted.org/packages/39/ca/67bb15a7061d6fe20b9b2a2fd783e296a1e0f93468252c093481a2f00efa/jiter-0.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:53839b35a38f56b8be26a7851a48b89bc47e5d88e900929df10ed93b95fea3d6", size = 317965, upload-time = "2025-11-09T20:48:03.783Z" },
927
+ { url = "https://files.pythonhosted.org/packages/18/af/1788031cd22e29c3b14bc6ca80b16a39a0b10e611367ffd480c06a259831/jiter-0.12.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94f669548e55c91ab47fef8bddd9c954dab1938644e715ea49d7e117015110a4", size = 345831, upload-time = "2025-11-09T20:48:05.55Z" },
928
+ { url = "https://files.pythonhosted.org/packages/05/17/710bf8472d1dff0d3caf4ced6031060091c1320f84ee7d5dcbed1f352417/jiter-0.12.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:351d54f2b09a41600ffea43d081522d792e81dcfb915f6d2d242744c1cc48beb", size = 361272, upload-time = "2025-11-09T20:48:06.951Z" },
929
+ { url = "https://files.pythonhosted.org/packages/fb/f1/1dcc4618b59761fef92d10bcbb0b038b5160be653b003651566a185f1a5c/jiter-0.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2a5e90604620f94bf62264e7c2c038704d38217b7465b863896c6d7c902b06c7", size = 204604, upload-time = "2025-11-09T20:48:08.328Z" },
930
+ { url = "https://files.pythonhosted.org/packages/d9/32/63cb1d9f1c5c6632a783c0052cde9ef7ba82688f7065e2f0d5f10a7e3edb/jiter-0.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:88ef757017e78d2860f96250f9393b7b577b06a956ad102c29c8237554380db3", size = 185628, upload-time = "2025-11-09T20:48:09.572Z" },
931
+ { url = "https://files.pythonhosted.org/packages/a8/99/45c9f0dbe4a1416b2b9a8a6d1236459540f43d7fb8883cff769a8db0612d/jiter-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:c46d927acd09c67a9fb1416df45c5a04c27e83aae969267e98fba35b74e99525", size = 312478, upload-time = "2025-11-09T20:48:10.898Z" },
932
+ { url = "https://files.pythonhosted.org/packages/4c/a7/54ae75613ba9e0f55fcb0bc5d1f807823b5167cc944e9333ff322e9f07dd/jiter-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:774ff60b27a84a85b27b88cd5583899c59940bcc126caca97eb2a9df6aa00c49", size = 318706, upload-time = "2025-11-09T20:48:12.266Z" },
933
+ { url = "https://files.pythonhosted.org/packages/59/31/2aa241ad2c10774baf6c37f8b8e1f39c07db358f1329f4eb40eba179c2a2/jiter-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5433fab222fb072237df3f637d01b81f040a07dcac1cb4a5c75c7aa9ed0bef1", size = 351894, upload-time = "2025-11-09T20:48:13.673Z" },
934
+ { url = "https://files.pythonhosted.org/packages/54/4f/0f2759522719133a9042781b18cc94e335b6d290f5e2d3e6899d6af933e3/jiter-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f8c593c6e71c07866ec6bfb790e202a833eeec885022296aff6b9e0b92d6a70e", size = 365714, upload-time = "2025-11-09T20:48:15.083Z" },
935
+ { url = "https://files.pythonhosted.org/packages/dc/6f/806b895f476582c62a2f52c453151edd8a0fde5411b0497baaa41018e878/jiter-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:90d32894d4c6877a87ae00c6b915b609406819dce8bc0d4e962e4de2784e567e", size = 478989, upload-time = "2025-11-09T20:48:16.706Z" },
936
+ { url = "https://files.pythonhosted.org/packages/86/6c/012d894dc6e1033acd8db2b8346add33e413ec1c7c002598915278a37f79/jiter-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:798e46eed9eb10c3adbbacbd3bdb5ecd4cf7064e453d00dbef08802dae6937ff", size = 378615, upload-time = "2025-11-09T20:48:18.614Z" },
937
+ { url = "https://files.pythonhosted.org/packages/87/30/d718d599f6700163e28e2c71c0bbaf6dace692e7df2592fd793ac9276717/jiter-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3f1368f0a6719ea80013a4eb90ba72e75d7ea67cfc7846db2ca504f3df0169a", size = 364745, upload-time = "2025-11-09T20:48:20.117Z" },
938
+ { url = "https://files.pythonhosted.org/packages/8f/85/315b45ce4b6ddc7d7fceca24068543b02bdc8782942f4ee49d652e2cc89f/jiter-0.12.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65f04a9d0b4406f7e51279710b27484af411896246200e461d80d3ba0caa901a", size = 386502, upload-time = "2025-11-09T20:48:21.543Z" },
939
+ { url = "https://files.pythonhosted.org/packages/74/0b/ce0434fb40c5b24b368fe81b17074d2840748b4952256bab451b72290a49/jiter-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:fd990541982a24281d12b67a335e44f117e4c6cbad3c3b75c7dea68bf4ce3a67", size = 519845, upload-time = "2025-11-09T20:48:22.964Z" },
940
+ { url = "https://files.pythonhosted.org/packages/e8/a3/7a7a4488ba052767846b9c916d208b3ed114e3eb670ee984e4c565b9cf0d/jiter-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b111b0e9152fa7df870ecaebb0bd30240d9f7fff1f2003bcb4ed0f519941820b", size = 510701, upload-time = "2025-11-09T20:48:24.483Z" },
941
+ { url = "https://files.pythonhosted.org/packages/c3/16/052ffbf9d0467b70af24e30f91e0579e13ded0c17bb4a8eb2aed3cb60131/jiter-0.12.0-cp314-cp314-win32.whl", hash = "sha256:a78befb9cc0a45b5a5a0d537b06f8544c2ebb60d19d02c41ff15da28a9e22d42", size = 205029, upload-time = "2025-11-09T20:48:25.749Z" },
942
+ { url = "https://files.pythonhosted.org/packages/e4/18/3cf1f3f0ccc789f76b9a754bdb7a6977e5d1d671ee97a9e14f7eb728d80e/jiter-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:e1fe01c082f6aafbe5c8faf0ff074f38dfb911d53f07ec333ca03f8f6226debf", size = 204960, upload-time = "2025-11-09T20:48:27.415Z" },
943
+ { url = "https://files.pythonhosted.org/packages/02/68/736821e52ecfdeeb0f024b8ab01b5a229f6b9293bbdb444c27efade50b0f/jiter-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:d72f3b5a432a4c546ea4bedc84cce0c3404874f1d1676260b9c7f048a9855451", size = 185529, upload-time = "2025-11-09T20:48:29.125Z" },
944
+ { url = "https://files.pythonhosted.org/packages/30/61/12ed8ee7a643cce29ac97c2281f9ce3956eb76b037e88d290f4ed0d41480/jiter-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e6ded41aeba3603f9728ed2b6196e4df875348ab97b28fc8afff115ed42ba7a7", size = 318974, upload-time = "2025-11-09T20:48:30.87Z" },
945
+ { url = "https://files.pythonhosted.org/packages/2d/c6/f3041ede6d0ed5e0e79ff0de4c8f14f401bbf196f2ef3971cdbe5fd08d1d/jiter-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a947920902420a6ada6ad51892082521978e9dd44a802663b001436e4b771684", size = 345932, upload-time = "2025-11-09T20:48:32.658Z" },
946
+ { url = "https://files.pythonhosted.org/packages/d5/5d/4d94835889edd01ad0e2dbfc05f7bdfaed46292e7b504a6ac7839aa00edb/jiter-0.12.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:add5e227e0554d3a52cf390a7635edaffdf4f8fce4fdbcef3cc2055bb396a30c", size = 367243, upload-time = "2025-11-09T20:48:34.093Z" },
947
+ { url = "https://files.pythonhosted.org/packages/fd/76/0051b0ac2816253a99d27baf3dda198663aff882fa6ea7deeb94046da24e/jiter-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f9b1cda8fcb736250d7e8711d4580ebf004a46771432be0ae4796944b5dfa5d", size = 479315, upload-time = "2025-11-09T20:48:35.507Z" },
948
+ { url = "https://files.pythonhosted.org/packages/70/ae/83f793acd68e5cb24e483f44f482a1a15601848b9b6f199dacb970098f77/jiter-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:deeb12a2223fe0135c7ff1356a143d57f95bbf1f4a66584f1fc74df21d86b993", size = 380714, upload-time = "2025-11-09T20:48:40.014Z" },
949
+ { url = "https://files.pythonhosted.org/packages/b1/5e/4808a88338ad2c228b1126b93fcd8ba145e919e886fe910d578230dabe3b/jiter-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c596cc0f4cb574877550ce4ecd51f8037469146addd676d7c1a30ebe6391923f", size = 365168, upload-time = "2025-11-09T20:48:41.462Z" },
950
+ { url = "https://files.pythonhosted.org/packages/0c/d4/04619a9e8095b42aef436b5aeb4c0282b4ff1b27d1db1508df9f5dc82750/jiter-0.12.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ab4c823b216a4aeab3fdbf579c5843165756bd9ad87cc6b1c65919c4715f783", size = 387893, upload-time = "2025-11-09T20:48:42.921Z" },
951
+ { url = "https://files.pythonhosted.org/packages/17/ea/d3c7e62e4546fdc39197fa4a4315a563a89b95b6d54c0d25373842a59cbe/jiter-0.12.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e427eee51149edf962203ff8db75a7514ab89be5cb623fb9cea1f20b54f1107b", size = 520828, upload-time = "2025-11-09T20:48:44.278Z" },
952
+ { url = "https://files.pythonhosted.org/packages/cc/0b/c6d3562a03fd767e31cb119d9041ea7958c3c80cb3d753eafb19b3b18349/jiter-0.12.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:edb868841f84c111255ba5e80339d386d937ec1fdce419518ce1bd9370fac5b6", size = 511009, upload-time = "2025-11-09T20:48:45.726Z" },
953
+ { url = "https://files.pythonhosted.org/packages/aa/51/2cb4468b3448a8385ebcd15059d325c9ce67df4e2758d133ab9442b19834/jiter-0.12.0-cp314-cp314t-win32.whl", hash = "sha256:8bbcfe2791dfdb7c5e48baf646d37a6a3dcb5a97a032017741dea9f817dca183", size = 205110, upload-time = "2025-11-09T20:48:47.033Z" },
954
+ { url = "https://files.pythonhosted.org/packages/b2/c5/ae5ec83dec9c2d1af805fd5fe8f74ebded9c8670c5210ec7820ce0dbeb1e/jiter-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2fa940963bf02e1d8226027ef461e36af472dea85d36054ff835aeed944dd873", size = 205223, upload-time = "2025-11-09T20:48:49.076Z" },
955
+ { url = "https://files.pythonhosted.org/packages/97/9a/3c5391907277f0e55195550cf3fa8e293ae9ee0c00fb402fec1e38c0c82f/jiter-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:506c9708dd29b27288f9f8f1140c3cb0e3d8ddb045956d7757b1fa0e0f39a473", size = 185564, upload-time = "2025-11-09T20:48:50.376Z" },
956
+ { url = "https://files.pythonhosted.org/packages/cb/f5/12efb8ada5f5c9edc1d4555fe383c1fb2eac05ac5859258a72d61981d999/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:e8547883d7b96ef2e5fe22b88f8a4c8725a56e7f4abafff20fd5272d634c7ecb", size = 309974, upload-time = "2025-11-09T20:49:17.187Z" },
957
+ { url = "https://files.pythonhosted.org/packages/85/15/d6eb3b770f6a0d332675141ab3962fd4a7c270ede3515d9f3583e1d28276/jiter-0.12.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:89163163c0934854a668ed783a2546a0617f71706a2551a4a0666d91ab365d6b", size = 304233, upload-time = "2025-11-09T20:49:18.734Z" },
958
+ { url = "https://files.pythonhosted.org/packages/8c/3e/e7e06743294eea2cf02ced6aa0ff2ad237367394e37a0e2b4a1108c67a36/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96b264ab7d34bbb2312dedc47ce07cd53f06835eacbc16dde3761f47c3a9e7f", size = 338537, upload-time = "2025-11-09T20:49:20.317Z" },
959
+ { url = "https://files.pythonhosted.org/packages/2f/9c/6753e6522b8d0ef07d3a3d239426669e984fb0eba15a315cdbc1253904e4/jiter-0.12.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24e864cb30ab82311c6425655b0cdab0a98c5d973b065c66a3f020740c2324c", size = 346110, upload-time = "2025-11-09T20:49:21.817Z" },
960
+ ]
961
+
962
  [[package]]
963
  name = "jsonschema"
964
  version = "4.25.1"
 
1303
  { url = "https://files.pythonhosted.org/packages/2d/fd/4b5eb0b3e888d86aee4d198c23acec7d214baaf17ea93c1adec94c9518b9/numpy-2.3.5-cp314-cp314t-win_arm64.whl", hash = "sha256:6203fdf9f3dc5bdaed7319ad8698e685c7a3be10819f41d32a0723e611733b42", size = 10545459, upload-time = "2025-11-16T22:52:20.55Z" },
1304
  ]
1305
 
1306
+ [[package]]
1307
+ name = "openai"
1308
+ version = "2.8.1"
1309
+ source = { registry = "https://pypi.org/simple" }
1310
+ dependencies = [
1311
+ { name = "anyio" },
1312
+ { name = "distro" },
1313
+ { name = "httpx" },
1314
+ { name = "jiter" },
1315
+ { name = "pydantic" },
1316
+ { name = "sniffio" },
1317
+ { name = "tqdm" },
1318
+ { name = "typing-extensions" },
1319
+ ]
1320
+ sdist = { url = "https://files.pythonhosted.org/packages/d5/e4/42591e356f1d53c568418dc7e30dcda7be31dd5a4d570bca22acb0525862/openai-2.8.1.tar.gz", hash = "sha256:cb1b79eef6e809f6da326a7ef6038719e35aa944c42d081807bfa1be8060f15f", size = 602490, upload-time = "2025-11-17T22:39:59.549Z" }
1321
+ wheels = [
1322
+ { url = "https://files.pythonhosted.org/packages/55/4f/dbc0c124c40cb390508a82770fb9f6e3ed162560181a85089191a851c59a/openai-2.8.1-py3-none-any.whl", hash = "sha256:c6c3b5a04994734386e8dad3c00a393f56d3b68a27cd2e8acae91a59e4122463", size = 1022688, upload-time = "2025-11-17T22:39:57.675Z" },
1323
+ ]
1324
+
1325
  [[package]]
1326
  name = "openapi-pydantic"
1327
  version = "0.5.1"