David Chu commited on
Commit
7be08b4
·
unverified ·
1 Parent(s): ef2f9cb

feat: adopt pydantic ai

Browse files

[PydanticAI](https://ai.pydantic.dev) provides structured output
for Google models and tracing on Logfire. It will also enable
easy switching between model providers.

Files changed (5) hide show
  1. app/agent.py +75 -87
  2. app/main.py +10 -4
  3. main.py +8 -5
  4. pyproject.toml +2 -0
  5. uv.lock +282 -11
app/agent.py CHANGED
@@ -1,101 +1,89 @@
1
- import json
2
- import re
3
  from pathlib import Path
4
 
5
- from google import genai
6
- from google.genai import types
7
- from pydantic import ValidationError
 
 
 
 
 
 
 
 
8
 
9
  from app import models
10
  from app.tools import dailymed, literature
11
 
12
- CONFIG = types.GenerateContentConfig(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  tools=[
14
  dailymed.find_drug_set_ids,
15
  dailymed.find_drug_instruction,
16
  literature.search_medical_literature,
17
  ],
18
- system_instruction=(Path(__file__).parent / "system_instruction.txt").read_text(),
19
- thinking_config=types.ThinkingConfig(include_thoughts=True, thinking_budget=1024),
20
  )
21
 
22
- RESPONSE_FORMAT = """\
23
-
24
- Return in JSON matching this specification:
25
-
26
- Statement = { "text": string, "sources": array<string> } // the `sources` array contains the ID of the sources
27
- Return: array<Statement>
28
-
29
- Do not return the response in a markdown code block.
30
- Do not include the citation in the `text` field."""
31
-
32
- SOURCE_TOOL_NAMES = {
33
- literature.search_medical_literature.__name__,
34
- dailymed.find_drug_set_ids.__name__,
35
- }
36
-
37
-
38
- def hydrate_sources(
39
- statements: list[dict], calling_history: list[types.Content]
40
- ) -> models.Statements:
41
- sources = {}
42
- for call in calling_history:
43
- for part in call.parts or []:
44
- if (
45
- (func := part.function_response)
46
- and func.name in SOURCE_TOOL_NAMES
47
- and func.response
48
- ):
49
- for source in func.response.get("result", []):
50
- sources[source["id"]] = source
51
-
52
- for statement in statements:
53
- if statement.get("sources"):
54
- statement_sources = []
55
- for source_id in statement["sources"]:
56
- try:
57
- statement_sources.append(sources[source_id])
58
- except KeyError:
59
- print("citaion hullucination")
60
- print(source_id)
61
- print(sources)
62
- statement["sources"] = statement_sources
63
-
64
- return models.Statements.model_validate({"statements": statements})
65
-
66
-
67
- def validate_response(response: types.GenerateContentResponse) -> models.Statements:
68
- thoughts = []
69
-
70
- for part in response.candidates[0].content.parts: # type: ignore
71
- if part.thought:
72
- thoughts.append(part.text)
73
-
74
- thoughts = " ".join(thoughts)
75
- text = (response.text or "").strip()
76
-
77
- # Extract content inside the first markdown code block (``` or ```json)
78
- match = re.match(r"^```(?:json)?\s*([\s\S]*?)\s*```", text)
79
- if match:
80
- text = match.group(1).strip()
81
-
82
- try:
83
- statements = hydrate_sources(
84
- json.loads(text), response.automatic_function_calling_history or []
85
- )
86
- statements.thoughts = thoughts
87
- except (json.decoder.JSONDecodeError, ValidationError):
88
- statements = models.Statements(
89
- statements=[models.Statement(text=text)],
90
- thoughts=thoughts,
91
- )
92
- return statements
93
-
94
-
95
- def respond(client: genai.Client, query: str) -> models.Statements:
96
- resp = client.models.generate_content(
97
- model="gemini-2.5-flash-preview-05-20",
98
- contents=query + RESPONSE_FORMAT,
99
- config=CONFIG,
100
  )
101
- return validate_response(resp)
 
 
 
1
  from pathlib import Path
2
 
3
+ import logfire
4
+ from pydantic import BaseModel, Field
5
+ from pydantic_ai import Agent
6
+ from pydantic_ai.messages import (
7
+ ModelMessage,
8
+ ModelRequest,
9
+ ModelResponse,
10
+ TextPart,
11
+ ToolReturnPart,
12
+ )
13
+ from pydantic_ai.models.google import GoogleModel, GoogleModelSettings
14
 
15
  from app import models
16
  from app.tools import dailymed, literature
17
 
18
+
19
+ class Context(BaseModel):
20
+ thoughts: list[str]
21
+ sources: dict[str, dict]
22
+
23
+
24
+ class Statement(BaseModel):
25
+ text: str
26
+ sources: list[str] | None = Field(
27
+ default=None, description="ID of the sources that support this statement."
28
+ )
29
+
30
+
31
+ model = GoogleModel("gemini-2.5-flash-preview-05-20")
32
+ settings = GoogleModelSettings(
33
+ google_thinking_config={"thinking_budget": 2048, "include_thoughts": True},
34
+ )
35
+ agent = Agent(
36
+ model=model,
37
+ name="elna",
38
+ model_settings=settings,
39
+ output_type=list[Statement],
40
+ system_prompt=(Path(__file__).parent / "system_instruction.txt").read_text(),
41
  tools=[
42
  dailymed.find_drug_set_ids,
43
  dailymed.find_drug_instruction,
44
  literature.search_medical_literature,
45
  ],
 
 
46
  )
47
 
48
+
49
+ def get_context(messages: list[ModelMessage]) -> Context:
50
+ thoughts: list[str] = []
51
+ sources: dict[str, dict] = {}
52
+
53
+ for message in messages:
54
+ if isinstance(message, ModelResponse):
55
+ for part in message.parts:
56
+ if isinstance(part, TextPart):
57
+ thoughts.append(part.content)
58
+ elif isinstance(message, ModelRequest):
59
+ for part in message.parts:
60
+ if isinstance(part, ToolReturnPart) and part.tool_name in {
61
+ "search_medical_literature",
62
+ "find_drug_set_ids",
63
+ }:
64
+ for item in part.content:
65
+ sources[item["id"]] = item
66
+
67
+ return Context(thoughts=thoughts, sources=sources)
68
+
69
+
70
+ def respond(query: str) -> models.Statements:
71
+ result = agent.run_sync(query)
72
+ context = get_context(result.all_messages())
73
+
74
+ statements = []
75
+ for statement in result.output:
76
+ sources = []
77
+ for source_id in statement.sources or []:
78
+ try:
79
+ sources.append(context.sources[source_id])
80
+ except KeyError:
81
+ logfire.warning(f"citation hallucination '{source_id}'")
82
+ statements.append({"text": statement.text, "sources": sources})
83
+
84
+ return models.Statements.model_validate(
85
+ {
86
+ "statements": statements,
87
+ "thoughts": "\n\n".join(context.thoughts),
88
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  )
 
app/main.py CHANGED
@@ -1,10 +1,16 @@
 
1
  from fastapi import FastAPI
2
- from google import genai
3
 
4
- from app import agent, config, models
 
 
 
 
 
 
5
 
6
- gemini = genai.Client(api_key=config.settings.google_api_key)
7
  app = FastAPI()
 
8
 
9
 
10
  @app.get("/health")
@@ -14,4 +20,4 @@ def health_check():
14
 
15
  @app.get("/ask", response_model=models.Statements)
16
  def ask(query: str):
17
- return agent.respond(gemini, query)
 
1
+ import logfire
2
  from fastapi import FastAPI
 
3
 
4
+ from app import agent, models
5
+
6
+ logfire.configure(
7
+ service_name="api", scrubbing=False, send_to_logfire="if-token-present"
8
+ )
9
+ logfire.instrument_pydantic_ai()
10
+
11
 
 
12
  app = FastAPI()
13
+ logfire.instrument_fastapi(app, capture_headers=True)
14
 
15
 
16
  @app.get("/health")
 
20
 
21
  @app.get("/ask", response_model=models.Statements)
22
  def ask(query: str):
23
+ return agent.respond(query)
main.py CHANGED
@@ -1,7 +1,12 @@
 
1
  import streamlit as st
2
- from google import genai
3
 
4
- from app import agent, config, models
 
 
 
 
 
5
 
6
 
7
  def format_output(statements: models.Statements) -> tuple[str, str]:
@@ -34,8 +39,6 @@ def format_output(statements: models.Statements) -> tuple[str, str]:
34
 
35
 
36
  def main():
37
- gemini_client = genai.Client(api_key=config.settings.google_api_key)
38
-
39
  st.title("Elna")
40
  with st.form("search", border=False):
41
  query = st.text_input("Your medical question")
@@ -43,7 +46,7 @@ def main():
43
 
44
  if submit:
45
  with st.spinner("Thinking...", show_time=True):
46
- output = agent.respond(gemini_client, query)
47
  answer, thoughts = format_output(output)
48
  with st.expander("Thinking Process"):
49
  st.markdown(thoughts)
 
1
+ import logfire
2
  import streamlit as st
 
3
 
4
+ from app import agent, models
5
+
6
+ logfire.configure(
7
+ service_name="streamlit", scrubbing=False, send_to_logfire="if-token-present"
8
+ )
9
+ logfire.instrument_pydantic_ai()
10
 
11
 
12
  def format_output(statements: models.Statements) -> tuple[str, str]:
 
39
 
40
 
41
  def main():
 
 
42
  st.title("Elna")
43
  with st.form("search", border=False):
44
  query = st.text_input("Your medical question")
 
46
 
47
  if submit:
48
  with st.spinner("Thinking...", show_time=True):
49
+ output = agent.respond(query)
50
  answer, thoughts = format_output(output)
51
  with st.expander("Thinking Process"):
52
  st.markdown(thoughts)
pyproject.toml CHANGED
@@ -8,7 +8,9 @@ dependencies = [
8
  "fastapi[standard]>=0.115.12",
9
  "google-genai>=1.14.0",
10
  "httpx>=0.28.1",
 
11
  "pydantic>=2.11.4",
 
12
  "pydantic-settings>=2.9.1",
13
  "python-fasthtml>=0.12.15",
14
  "streamlit>=1.45.0",
 
8
  "fastapi[standard]>=0.115.12",
9
  "google-genai>=1.14.0",
10
  "httpx>=0.28.1",
11
+ "logfire>=3.18.0",
12
  "pydantic>=2.11.4",
13
+ "pydantic-ai-slim[google]>=0.2.16",
14
  "pydantic-settings>=2.9.1",
15
  "python-fasthtml>=0.12.15",
16
  "streamlit>=1.45.0",
uv.lock CHANGED
@@ -204,7 +204,9 @@ dependencies = [
204
  { name = "fastapi", extra = ["standard"] },
205
  { name = "google-genai" },
206
  { name = "httpx" },
 
207
  { name = "pydantic" },
 
208
  { name = "pydantic-settings" },
209
  { name = "python-fasthtml" },
210
  { name = "streamlit" },
@@ -216,7 +218,9 @@ requires-dist = [
216
  { name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" },
217
  { name = "google-genai", specifier = ">=1.14.0" },
218
  { name = "httpx", specifier = ">=0.28.1" },
 
219
  { name = "pydantic", specifier = ">=2.11.4" },
 
220
  { name = "pydantic-settings", specifier = ">=2.9.1" },
221
  { name = "python-fasthtml", specifier = ">=0.12.15" },
222
  { name = "streamlit", specifier = ">=1.45.0" },
@@ -236,6 +240,24 @@ wheels = [
236
  { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521, upload-time = "2024-06-20T11:30:28.248Z" },
237
  ]
238
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  [[package]]
240
  name = "fastapi"
241
  version = "0.115.12"
@@ -344,7 +366,7 @@ wheels = [
344
 
345
  [[package]]
346
  name = "google-genai"
347
- version = "1.14.0"
348
  source = { registry = "https://pypi.org/simple" }
349
  dependencies = [
350
  { name = "anyio" },
@@ -355,9 +377,33 @@ dependencies = [
355
  { name = "typing-extensions" },
356
  { name = "websockets" },
357
  ]
358
- sdist = { url = "https://files.pythonhosted.org/packages/00/ba/c8e4c0b60c6dda40e51e2125709d097c2609fce1389b4a05f40cdd51c1ec/google_genai-1.14.0.tar.gz", hash = "sha256:7c608de5bb173486a546f5ec4562255c26bae72d33d758a3207bb26f695d0087", size = 169938, upload-time = "2025-05-07T22:50:03.113Z" }
359
  wheels = [
360
- { url = "https://files.pythonhosted.org/packages/12/86/dde4cd028c8b0716b3f1f6d202647396a87a4ecbbdc7e4beb59b9d9284d3/google_genai-1.14.0-py3-none-any.whl", hash = "sha256:5916ee985bf69ac7b68c4488949225db71e21579afc7ba5ecd5321173b60d3b2", size = 168862, upload-time = "2025-05-07T22:50:01.296Z" },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  ]
362
 
363
  [[package]]
@@ -428,6 +474,18 @@ wheels = [
428
  { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
429
  ]
430
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  [[package]]
432
  name = "itsdangerous"
433
  version = "2.2.0"
@@ -476,6 +534,33 @@ wheels = [
476
  { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" },
477
  ]
478
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  [[package]]
480
  name = "markdown-it-py"
481
  version = "3.0.0"
@@ -591,6 +676,103 @@ wheels = [
591
  { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688, upload-time = "2022-10-17T20:04:24.037Z" },
592
  ]
593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
594
  [[package]]
595
  name = "packaging"
596
  version = "24.2"
@@ -677,16 +859,16 @@ wheels = [
677
 
678
  [[package]]
679
  name = "protobuf"
680
- version = "6.30.2"
681
  source = { registry = "https://pypi.org/simple" }
682
- sdist = { url = "https://files.pythonhosted.org/packages/c8/8c/cf2ac658216eebe49eaedf1e06bc06cbf6a143469236294a1171a51357c3/protobuf-6.30.2.tar.gz", hash = "sha256:35c859ae076d8c56054c25b59e5e59638d86545ed6e2b6efac6be0b6ea3ba048", size = 429315, upload-time = "2025-03-26T19:12:57.394Z" }
683
  wheels = [
684
- { url = "https://files.pythonhosted.org/packages/be/85/cd53abe6a6cbf2e0029243d6ae5fb4335da2996f6c177bb2ce685068e43d/protobuf-6.30.2-cp310-abi3-win32.whl", hash = "sha256:b12ef7df7b9329886e66404bef5e9ce6a26b54069d7f7436a0853ccdeb91c103", size = 419148, upload-time = "2025-03-26T19:12:41.359Z" },
685
- { url = "https://files.pythonhosted.org/packages/97/e9/7b9f1b259d509aef2b833c29a1f3c39185e2bf21c9c1be1cd11c22cb2149/protobuf-6.30.2-cp310-abi3-win_amd64.whl", hash = "sha256:7653c99774f73fe6b9301b87da52af0e69783a2e371e8b599b3e9cb4da4b12b9", size = 431003, upload-time = "2025-03-26T19:12:44.156Z" },
686
- { url = "https://files.pythonhosted.org/packages/8e/66/7f3b121f59097c93267e7f497f10e52ced7161b38295137a12a266b6c149/protobuf-6.30.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:0eb523c550a66a09a0c20f86dd554afbf4d32b02af34ae53d93268c1f73bc65b", size = 417579, upload-time = "2025-03-26T19:12:45.447Z" },
687
- { url = "https://files.pythonhosted.org/packages/d0/89/bbb1bff09600e662ad5b384420ad92de61cab2ed0f12ace1fd081fd4c295/protobuf-6.30.2-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:50f32cc9fd9cb09c783ebc275611b4f19dfdfb68d1ee55d2f0c7fa040df96815", size = 317319, upload-time = "2025-03-26T19:12:46.999Z" },
688
- { url = "https://files.pythonhosted.org/packages/28/50/1925de813499546bc8ab3ae857e3ec84efe7d2f19b34529d0c7c3d02d11d/protobuf-6.30.2-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4f6c687ae8efae6cf6093389a596548214467778146b7245e886f35e1485315d", size = 316212, upload-time = "2025-03-26T19:12:48.458Z" },
689
- { url = "https://files.pythonhosted.org/packages/e5/a1/93c2acf4ade3c5b557d02d500b06798f4ed2c176fa03e3c34973ca92df7f/protobuf-6.30.2-py3-none-any.whl", hash = "sha256:ae86b030e69a98e08c77beab574cbcb9fff6d031d57209f574a5aea1445f4b51", size = 167062, upload-time = "2025-03-26T19:12:55.892Z" },
690
  ]
691
 
692
  [[package]]
@@ -760,6 +942,29 @@ wheels = [
760
  { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" },
761
  ]
762
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
763
  [[package]]
764
  name = "pydantic-core"
765
  version = "2.33.2"
@@ -802,6 +1007,21 @@ wheels = [
802
  { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
803
  ]
804
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
805
  [[package]]
806
  name = "pydantic-settings"
807
  version = "2.9.1"
@@ -1343,3 +1563,54 @@ wheels = [
1343
  { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
1344
  { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
1345
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  { name = "fastapi", extra = ["standard"] },
205
  { name = "google-genai" },
206
  { name = "httpx" },
207
+ { name = "logfire" },
208
  { name = "pydantic" },
209
+ { name = "pydantic-ai-slim", extra = ["google"] },
210
  { name = "pydantic-settings" },
211
  { name = "python-fasthtml" },
212
  { name = "streamlit" },
 
218
  { name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" },
219
  { name = "google-genai", specifier = ">=1.14.0" },
220
  { name = "httpx", specifier = ">=0.28.1" },
221
+ { name = "logfire", specifier = ">=3.18.0" },
222
  { name = "pydantic", specifier = ">=2.11.4" },
223
+ { name = "pydantic-ai-slim", extras = ["google"], specifier = ">=0.2.16" },
224
  { name = "pydantic-settings", specifier = ">=2.9.1" },
225
  { name = "python-fasthtml", specifier = ">=0.12.15" },
226
  { name = "streamlit", specifier = ">=1.45.0" },
 
240
  { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521, upload-time = "2024-06-20T11:30:28.248Z" },
241
  ]
242
 
243
+ [[package]]
244
+ name = "eval-type-backport"
245
+ version = "0.2.2"
246
+ source = { registry = "https://pypi.org/simple" }
247
+ sdist = { url = "https://files.pythonhosted.org/packages/30/ea/8b0ac4469d4c347c6a385ff09dc3c048c2d021696664e26c7ee6791631b5/eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1", size = 9079, upload-time = "2024-12-21T20:09:46.005Z" }
248
+ wheels = [
249
+ { url = "https://files.pythonhosted.org/packages/ce/31/55cd413eaccd39125368be33c46de24a1f639f2e12349b0361b4678f3915/eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a", size = 5830, upload-time = "2024-12-21T20:09:44.175Z" },
250
+ ]
251
+
252
+ [[package]]
253
+ name = "executing"
254
+ version = "2.2.0"
255
+ source = { registry = "https://pypi.org/simple" }
256
+ sdist = { url = "https://files.pythonhosted.org/packages/91/50/a9d80c47ff289c611ff12e63f7c5d13942c65d68125160cefd768c73e6e4/executing-2.2.0.tar.gz", hash = "sha256:5d108c028108fe2551d1a7b2e8b713341e2cb4fc0aa7dcf966fa4327a5226755", size = 978693, upload-time = "2025-01-22T15:41:29.403Z" }
257
+ wheels = [
258
+ { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" },
259
+ ]
260
+
261
  [[package]]
262
  name = "fastapi"
263
  version = "0.115.12"
 
366
 
367
  [[package]]
368
  name = "google-genai"
369
+ version = "1.19.0"
370
  source = { registry = "https://pypi.org/simple" }
371
  dependencies = [
372
  { name = "anyio" },
 
377
  { name = "typing-extensions" },
378
  { name = "websockets" },
379
  ]
380
+ sdist = { url = "https://files.pythonhosted.org/packages/14/17/8f717f43732ae2b7775f816f0d8f0b39e2a020bbe7ba202f2ddb2f948c3b/google_genai-1.19.0.tar.gz", hash = "sha256:66f5de78075781bfd9e423f1e3592e4240759dfe0ac42ac74a9dcb2c4f662e9d", size = 198000, upload-time = "2025-06-04T23:10:04.69Z" }
381
  wheels = [
382
+ { url = "https://files.pythonhosted.org/packages/c4/ae/64fccdebf5811453ce53b0d5ee23d4f27ef173ef36d3b67dad791a0007aa/google_genai-1.19.0-py3-none-any.whl", hash = "sha256:a2955612e4af8c84f83eb43c1ce4e74e1b714732926d0705e639761938192466", size = 200043, upload-time = "2025-06-04T23:10:02.692Z" },
383
+ ]
384
+
385
+ [[package]]
386
+ name = "googleapis-common-protos"
387
+ version = "1.70.0"
388
+ source = { registry = "https://pypi.org/simple" }
389
+ dependencies = [
390
+ { name = "protobuf" },
391
+ ]
392
+ sdist = { url = "https://files.pythonhosted.org/packages/39/24/33db22342cf4a2ea27c9955e6713140fedd51e8b141b5ce5260897020f1a/googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257", size = 145903, upload-time = "2025-04-14T10:17:02.924Z" }
393
+ wheels = [
394
+ { url = "https://files.pythonhosted.org/packages/86/f1/62a193f0227cf15a920390abe675f386dec35f7ae3ffe6da582d3ade42c7/googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8", size = 294530, upload-time = "2025-04-14T10:17:01.271Z" },
395
+ ]
396
+
397
+ [[package]]
398
+ name = "griffe"
399
+ version = "1.7.3"
400
+ source = { registry = "https://pypi.org/simple" }
401
+ dependencies = [
402
+ { name = "colorama" },
403
+ ]
404
+ sdist = { url = "https://files.pythonhosted.org/packages/a9/3e/5aa9a61f7c3c47b0b52a1d930302992229d191bf4bc76447b324b731510a/griffe-1.7.3.tar.gz", hash = "sha256:52ee893c6a3a968b639ace8015bec9d36594961e156e23315c8e8e51401fa50b", size = 395137, upload-time = "2025-04-23T11:29:09.147Z" }
405
+ wheels = [
406
+ { url = "https://files.pythonhosted.org/packages/58/c6/5c20af38c2a57c15d87f7f38bee77d63c1d2a3689f74fefaf35915dd12b2/griffe-1.7.3-py3-none-any.whl", hash = "sha256:c6b3ee30c2f0f17f30bcdef5068d6ab7a2a4f1b8bf1a3e74b56fffd21e1c5f75", size = 129303, upload-time = "2025-04-23T11:29:07.145Z" },
407
  ]
408
 
409
  [[package]]
 
474
  { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
475
  ]
476
 
477
+ [[package]]
478
+ name = "importlib-metadata"
479
+ version = "8.7.0"
480
+ source = { registry = "https://pypi.org/simple" }
481
+ dependencies = [
482
+ { name = "zipp" },
483
+ ]
484
+ sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" }
485
+ wheels = [
486
+ { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" },
487
+ ]
488
+
489
  [[package]]
490
  name = "itsdangerous"
491
  version = "2.2.0"
 
534
  { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437, upload-time = "2025-04-23T12:34:05.422Z" },
535
  ]
536
 
537
+ [[package]]
538
+ name = "logfire"
539
+ version = "3.18.0"
540
+ source = { registry = "https://pypi.org/simple" }
541
+ dependencies = [
542
+ { name = "executing" },
543
+ { name = "opentelemetry-exporter-otlp-proto-http" },
544
+ { name = "opentelemetry-instrumentation" },
545
+ { name = "opentelemetry-sdk" },
546
+ { name = "protobuf" },
547
+ { name = "rich" },
548
+ { name = "typing-extensions" },
549
+ ]
550
+ sdist = { url = "https://files.pythonhosted.org/packages/c7/52/b39eb4b8f4f9d5066c5b91c4ad6aab4e71e69d87a69b9b55ed05376accb9/logfire-3.18.0.tar.gz", hash = "sha256:86efe521e8d7161edb66c8f4839efcd46e3ef4b17f641b96bf05111807df4bd5", size = 486165, upload-time = "2025-06-05T14:30:32.941Z" }
551
+ wheels = [
552
+ { url = "https://files.pythonhosted.org/packages/4d/38/1b26834130d5aef342ba828ea3c794696e3637aa8a2bd3288aa100e3a73e/logfire-3.18.0-py3-none-any.whl", hash = "sha256:da1a4d15679c19391f423ed76c9a7b730a00bdc639cfdc09ef5b4c05b7cb6b6d", size = 197653, upload-time = "2025-06-05T14:30:29.515Z" },
553
+ ]
554
+
555
+ [[package]]
556
+ name = "logfire-api"
557
+ version = "3.18.0"
558
+ source = { registry = "https://pypi.org/simple" }
559
+ sdist = { url = "https://files.pythonhosted.org/packages/16/b5/0893c970673c0a3ca422edd2488db0e0b9f1126ad036efe79f1f833ea3f5/logfire_api-3.18.0.tar.gz", hash = "sha256:87dbae56c8d50560a20ce67a9ef08ee208506fa0dd2c0c3c6b3d709dde1d9795", size = 48618, upload-time = "2025-06-05T14:30:34.873Z" }
560
+ wheels = [
561
+ { url = "https://files.pythonhosted.org/packages/95/10/35f0e4811c55264e7c45200a90807877f755c955a44e0f3e9a0e7e081b9a/logfire_api-3.18.0-py3-none-any.whl", hash = "sha256:9c23ac20dbaecf95a5aee87944839543f02ecc6f89369bbd23bb0b2ce4e52ed7", size = 80869, upload-time = "2025-06-05T14:30:31.612Z" },
562
+ ]
563
+
564
  [[package]]
565
  name = "markdown-it-py"
566
  version = "3.0.0"
 
676
  { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688, upload-time = "2022-10-17T20:04:24.037Z" },
677
  ]
678
 
679
+ [[package]]
680
+ name = "opentelemetry-api"
681
+ version = "1.34.1"
682
+ source = { registry = "https://pypi.org/simple" }
683
+ dependencies = [
684
+ { name = "importlib-metadata" },
685
+ { name = "typing-extensions" },
686
+ ]
687
+ sdist = { url = "https://files.pythonhosted.org/packages/4d/5e/94a8cb759e4e409022229418294e098ca7feca00eb3c467bb20cbd329bda/opentelemetry_api-1.34.1.tar.gz", hash = "sha256:64f0bd06d42824843731d05beea88d4d4b6ae59f9fe347ff7dfa2cc14233bbb3", size = 64987, upload-time = "2025-06-10T08:55:19.818Z" }
688
+ wheels = [
689
+ { url = "https://files.pythonhosted.org/packages/a5/3a/2ba85557e8dc024c0842ad22c570418dc02c36cbd1ab4b832a93edf071b8/opentelemetry_api-1.34.1-py3-none-any.whl", hash = "sha256:b7df4cb0830d5a6c29ad0c0691dbae874d8daefa934b8b1d642de48323d32a8c", size = 65767, upload-time = "2025-06-10T08:54:56.717Z" },
690
+ ]
691
+
692
+ [[package]]
693
+ name = "opentelemetry-exporter-otlp-proto-common"
694
+ version = "1.34.1"
695
+ source = { registry = "https://pypi.org/simple" }
696
+ dependencies = [
697
+ { name = "opentelemetry-proto" },
698
+ ]
699
+ sdist = { url = "https://files.pythonhosted.org/packages/86/f0/ff235936ee40db93360233b62da932d4fd9e8d103cd090c6bcb9afaf5f01/opentelemetry_exporter_otlp_proto_common-1.34.1.tar.gz", hash = "sha256:b59a20a927facd5eac06edaf87a07e49f9e4a13db487b7d8a52b37cb87710f8b", size = 20817, upload-time = "2025-06-10T08:55:22.55Z" }
700
+ wheels = [
701
+ { url = "https://files.pythonhosted.org/packages/72/e8/8b292a11cc8d8d87ec0c4089ae21b6a58af49ca2e51fa916435bc922fdc7/opentelemetry_exporter_otlp_proto_common-1.34.1-py3-none-any.whl", hash = "sha256:8e2019284bf24d3deebbb6c59c71e6eef3307cd88eff8c633e061abba33f7e87", size = 18834, upload-time = "2025-06-10T08:55:00.806Z" },
702
+ ]
703
+
704
+ [[package]]
705
+ name = "opentelemetry-exporter-otlp-proto-http"
706
+ version = "1.34.1"
707
+ source = { registry = "https://pypi.org/simple" }
708
+ dependencies = [
709
+ { name = "googleapis-common-protos" },
710
+ { name = "opentelemetry-api" },
711
+ { name = "opentelemetry-exporter-otlp-proto-common" },
712
+ { name = "opentelemetry-proto" },
713
+ { name = "opentelemetry-sdk" },
714
+ { name = "requests" },
715
+ { name = "typing-extensions" },
716
+ ]
717
+ sdist = { url = "https://files.pythonhosted.org/packages/19/8f/954bc725961cbe425a749d55c0ba1df46832a5999eae764d1a7349ac1c29/opentelemetry_exporter_otlp_proto_http-1.34.1.tar.gz", hash = "sha256:aaac36fdce46a8191e604dcf632e1f9380c7d5b356b27b3e0edb5610d9be28ad", size = 15351, upload-time = "2025-06-10T08:55:24.657Z" }
718
+ wheels = [
719
+ { url = "https://files.pythonhosted.org/packages/79/54/b05251c04e30c1ac70cf4a7c5653c085dfcf2c8b98af71661d6a252adc39/opentelemetry_exporter_otlp_proto_http-1.34.1-py3-none-any.whl", hash = "sha256:5251f00ca85872ce50d871f6d3cc89fe203b94c3c14c964bbdc3883366c705d8", size = 17744, upload-time = "2025-06-10T08:55:03.802Z" },
720
+ ]
721
+
722
+ [[package]]
723
+ name = "opentelemetry-instrumentation"
724
+ version = "0.55b1"
725
+ source = { registry = "https://pypi.org/simple" }
726
+ dependencies = [
727
+ { name = "opentelemetry-api" },
728
+ { name = "opentelemetry-semantic-conventions" },
729
+ { name = "packaging" },
730
+ { name = "wrapt" },
731
+ ]
732
+ sdist = { url = "https://files.pythonhosted.org/packages/cb/69/d8995f229ddf4d98b9c85dd126aeca03dd1742f6dc5d3bc0d2f6dae1535c/opentelemetry_instrumentation-0.55b1.tar.gz", hash = "sha256:2dc50aa207b9bfa16f70a1a0571e011e737a9917408934675b89ef4d5718c87b", size = 28552, upload-time = "2025-06-10T08:58:15.312Z" }
733
+ wheels = [
734
+ { url = "https://files.pythonhosted.org/packages/60/7d/8ddfda1506c2fcca137924d5688ccabffa1aed9ec0955b7d0772de02cec3/opentelemetry_instrumentation-0.55b1-py3-none-any.whl", hash = "sha256:cbb1496b42bc394e01bc63701b10e69094e8564e281de063e4328d122cc7a97e", size = 31108, upload-time = "2025-06-10T08:57:14.355Z" },
735
+ ]
736
+
737
+ [[package]]
738
+ name = "opentelemetry-proto"
739
+ version = "1.34.1"
740
+ source = { registry = "https://pypi.org/simple" }
741
+ dependencies = [
742
+ { name = "protobuf" },
743
+ ]
744
+ sdist = { url = "https://files.pythonhosted.org/packages/66/b3/c3158dd012463bb7c0eb7304a85a6f63baeeb5b4c93a53845cf89f848c7e/opentelemetry_proto-1.34.1.tar.gz", hash = "sha256:16286214e405c211fc774187f3e4bbb1351290b8dfb88e8948af209ce85b719e", size = 34344, upload-time = "2025-06-10T08:55:32.25Z" }
745
+ wheels = [
746
+ { url = "https://files.pythonhosted.org/packages/28/ab/4591bfa54e946350ce8b3f28e5c658fe9785e7cd11e9c11b1671a867822b/opentelemetry_proto-1.34.1-py3-none-any.whl", hash = "sha256:eb4bb5ac27f2562df2d6857fc557b3a481b5e298bc04f94cc68041f00cebcbd2", size = 55692, upload-time = "2025-06-10T08:55:14.904Z" },
747
+ ]
748
+
749
+ [[package]]
750
+ name = "opentelemetry-sdk"
751
+ version = "1.34.1"
752
+ source = { registry = "https://pypi.org/simple" }
753
+ dependencies = [
754
+ { name = "opentelemetry-api" },
755
+ { name = "opentelemetry-semantic-conventions" },
756
+ { name = "typing-extensions" },
757
+ ]
758
+ sdist = { url = "https://files.pythonhosted.org/packages/6f/41/fe20f9036433da8e0fcef568984da4c1d1c771fa072ecd1a4d98779dccdd/opentelemetry_sdk-1.34.1.tar.gz", hash = "sha256:8091db0d763fcd6098d4781bbc80ff0971f94e260739aa6afe6fd379cdf3aa4d", size = 159441, upload-time = "2025-06-10T08:55:33.028Z" }
759
+ wheels = [
760
+ { url = "https://files.pythonhosted.org/packages/07/1b/def4fe6aa73f483cabf4c748f4c25070d5f7604dcc8b52e962983491b29e/opentelemetry_sdk-1.34.1-py3-none-any.whl", hash = "sha256:308effad4059562f1d92163c61c8141df649da24ce361827812c40abb2a1e96e", size = 118477, upload-time = "2025-06-10T08:55:16.02Z" },
761
+ ]
762
+
763
+ [[package]]
764
+ name = "opentelemetry-semantic-conventions"
765
+ version = "0.55b1"
766
+ source = { registry = "https://pypi.org/simple" }
767
+ dependencies = [
768
+ { name = "opentelemetry-api" },
769
+ { name = "typing-extensions" },
770
+ ]
771
+ sdist = { url = "https://files.pythonhosted.org/packages/5d/f0/f33458486da911f47c4aa6db9bda308bb80f3236c111bf848bd870c16b16/opentelemetry_semantic_conventions-0.55b1.tar.gz", hash = "sha256:ef95b1f009159c28d7a7849f5cbc71c4c34c845bb514d66adfdf1b3fff3598b3", size = 119829, upload-time = "2025-06-10T08:55:33.881Z" }
772
+ wheels = [
773
+ { url = "https://files.pythonhosted.org/packages/1a/89/267b0af1b1d0ba828f0e60642b6a5116ac1fd917cde7fc02821627029bd1/opentelemetry_semantic_conventions-0.55b1-py3-none-any.whl", hash = "sha256:5da81dfdf7d52e3d37f8fe88d5e771e191de924cfff5f550ab0b8f7b2409baed", size = 196223, upload-time = "2025-06-10T08:55:17.638Z" },
774
+ ]
775
+
776
  [[package]]
777
  name = "packaging"
778
  version = "24.2"
 
859
 
860
  [[package]]
861
  name = "protobuf"
862
+ version = "5.29.5"
863
  source = { registry = "https://pypi.org/simple" }
864
+ sdist = { url = "https://files.pythonhosted.org/packages/43/29/d09e70352e4e88c9c7a198d5645d7277811448d76c23b00345670f7c8a38/protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84", size = 425226, upload-time = "2025-05-28T23:51:59.82Z" }
865
  wheels = [
866
+ { url = "https://files.pythonhosted.org/packages/5f/11/6e40e9fc5bba02988a214c07cf324595789ca7820160bfd1f8be96e48539/protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079", size = 422963, upload-time = "2025-05-28T23:51:41.204Z" },
867
+ { url = "https://files.pythonhosted.org/packages/81/7f/73cefb093e1a2a7c3ffd839e6f9fcafb7a427d300c7f8aef9c64405d8ac6/protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc", size = 434818, upload-time = "2025-05-28T23:51:44.297Z" },
868
+ { url = "https://files.pythonhosted.org/packages/dd/73/10e1661c21f139f2c6ad9b23040ff36fee624310dc28fba20d33fdae124c/protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671", size = 418091, upload-time = "2025-05-28T23:51:45.907Z" },
869
+ { url = "https://files.pythonhosted.org/packages/6c/04/98f6f8cf5b07ab1294c13f34b4e69b3722bb609c5b701d6c169828f9f8aa/protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015", size = 319824, upload-time = "2025-05-28T23:51:47.545Z" },
870
+ { url = "https://files.pythonhosted.org/packages/85/e4/07c80521879c2d15f321465ac24c70efe2381378c00bf5e56a0f4fbac8cd/protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61", size = 319942, upload-time = "2025-05-28T23:51:49.11Z" },
871
+ { url = "https://files.pythonhosted.org/packages/7e/cc/7e77861000a0691aeea8f4566e5d3aa716f2b1dece4a24439437e41d3d25/protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5", size = 172823, upload-time = "2025-05-28T23:51:58.157Z" },
872
  ]
873
 
874
  [[package]]
 
942
  { url = "https://files.pythonhosted.org/packages/e7/12/46b65f3534d099349e38ef6ec98b1a5a81f42536d17e0ba382c28c67ba67/pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb", size = 443900, upload-time = "2025-04-29T20:38:52.724Z" },
943
  ]
944
 
945
+ [[package]]
946
+ name = "pydantic-ai-slim"
947
+ version = "0.2.16"
948
+ source = { registry = "https://pypi.org/simple" }
949
+ dependencies = [
950
+ { name = "eval-type-backport" },
951
+ { name = "griffe" },
952
+ { name = "httpx" },
953
+ { name = "opentelemetry-api" },
954
+ { name = "pydantic" },
955
+ { name = "pydantic-graph" },
956
+ { name = "typing-inspection" },
957
+ ]
958
+ sdist = { url = "https://files.pythonhosted.org/packages/97/1d/159d6707c14672ab35072dbf1d22214eb45993fa4f4a89b946e8bb915417/pydantic_ai_slim-0.2.16.tar.gz", hash = "sha256:fcd8d2d596d554f8471e64909e3a354172a174ede2e09f898557bd2cf3142f6a", size = 139744, upload-time = "2025-06-08T20:09:50.309Z" }
959
+ wheels = [
960
+ { url = "https://files.pythonhosted.org/packages/5f/f6/55ff99c2c3512ca9140c5d7d3260937c4c742b8b1577a84fc6592974959d/pydantic_ai_slim-0.2.16-py3-none-any.whl", hash = "sha256:bcc2691003ca982e64f0fbcf207968e45b97fc91b4e113bfeabf67e49f8a9dd5", size = 189409, upload-time = "2025-06-08T20:09:38.341Z" },
961
+ ]
962
+
963
+ [package.optional-dependencies]
964
+ google = [
965
+ { name = "google-genai" },
966
+ ]
967
+
968
  [[package]]
969
  name = "pydantic-core"
970
  version = "2.33.2"
 
1007
  { url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777, upload-time = "2025-04-23T18:32:25.088Z" },
1008
  ]
1009
 
1010
+ [[package]]
1011
+ name = "pydantic-graph"
1012
+ version = "0.2.16"
1013
+ source = { registry = "https://pypi.org/simple" }
1014
+ dependencies = [
1015
+ { name = "httpx" },
1016
+ { name = "logfire-api" },
1017
+ { name = "pydantic" },
1018
+ { name = "typing-inspection" },
1019
+ ]
1020
+ sdist = { url = "https://files.pythonhosted.org/packages/6c/8a/ea3ed8c500e7092d9da32b3a54b02db7e79a8d675f5b920f48a807a5d84f/pydantic_graph-0.2.16.tar.gz", hash = "sha256:c98e254d5239767d3a27068fed017717039241829731480d892f66a0e25b9ce7", size = 21844, upload-time = "2025-06-08T20:09:52.328Z" }
1021
+ wheels = [
1022
+ { url = "https://files.pythonhosted.org/packages/08/28/b1e8d0ec29ae8b3a3c5a74a2ec51034994a464a847127b048be278983402/pydantic_graph-0.2.16-py3-none-any.whl", hash = "sha256:56ea080a479bda04be6cffb9a2ea0173e16826efa47e93b8b4e20a66ae067e33", size = 27487, upload-time = "2025-06-08T20:09:41.217Z" },
1023
+ ]
1024
+
1025
  [[package]]
1026
  name = "pydantic-settings"
1027
  version = "2.9.1"
 
1563
  { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" },
1564
  { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" },
1565
  ]
1566
+
1567
+ [[package]]
1568
+ name = "wrapt"
1569
+ version = "1.17.2"
1570
+ source = { registry = "https://pypi.org/simple" }
1571
+ sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531, upload-time = "2025-01-14T10:35:45.465Z" }
1572
+ wheels = [
1573
+ { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799, upload-time = "2025-01-14T10:33:57.4Z" },
1574
+ { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821, upload-time = "2025-01-14T10:33:59.334Z" },
1575
+ { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919, upload-time = "2025-01-14T10:34:04.093Z" },
1576
+ { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721, upload-time = "2025-01-14T10:34:07.163Z" },
1577
+ { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899, upload-time = "2025-01-14T10:34:09.82Z" },
1578
+ { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222, upload-time = "2025-01-14T10:34:11.258Z" },
1579
+ { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707, upload-time = "2025-01-14T10:34:12.49Z" },
1580
+ { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685, upload-time = "2025-01-14T10:34:15.043Z" },
1581
+ { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567, upload-time = "2025-01-14T10:34:16.563Z" },
1582
+ { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672, upload-time = "2025-01-14T10:34:17.727Z" },
1583
+ { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865, upload-time = "2025-01-14T10:34:19.577Z" },
1584
+ { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800, upload-time = "2025-01-14T10:34:21.571Z" },
1585
+ { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824, upload-time = "2025-01-14T10:34:22.999Z" },
1586
+ { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920, upload-time = "2025-01-14T10:34:25.386Z" },
1587
+ { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690, upload-time = "2025-01-14T10:34:28.058Z" },
1588
+ { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861, upload-time = "2025-01-14T10:34:29.167Z" },
1589
+ { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174, upload-time = "2025-01-14T10:34:31.702Z" },
1590
+ { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721, upload-time = "2025-01-14T10:34:32.91Z" },
1591
+ { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763, upload-time = "2025-01-14T10:34:34.903Z" },
1592
+ { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585, upload-time = "2025-01-14T10:34:36.13Z" },
1593
+ { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676, upload-time = "2025-01-14T10:34:37.962Z" },
1594
+ { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871, upload-time = "2025-01-14T10:34:39.13Z" },
1595
+ { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312, upload-time = "2025-01-14T10:34:40.604Z" },
1596
+ { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062, upload-time = "2025-01-14T10:34:45.011Z" },
1597
+ { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155, upload-time = "2025-01-14T10:34:47.25Z" },
1598
+ { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471, upload-time = "2025-01-14T10:34:50.934Z" },
1599
+ { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208, upload-time = "2025-01-14T10:34:52.297Z" },
1600
+ { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339, upload-time = "2025-01-14T10:34:53.489Z" },
1601
+ { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232, upload-time = "2025-01-14T10:34:55.327Z" },
1602
+ { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476, upload-time = "2025-01-14T10:34:58.055Z" },
1603
+ { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377, upload-time = "2025-01-14T10:34:59.3Z" },
1604
+ { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986, upload-time = "2025-01-14T10:35:00.498Z" },
1605
+ { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750, upload-time = "2025-01-14T10:35:03.378Z" },
1606
+ { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594, upload-time = "2025-01-14T10:35:44.018Z" },
1607
+ ]
1608
+
1609
+ [[package]]
1610
+ name = "zipp"
1611
+ version = "3.23.0"
1612
+ source = { registry = "https://pypi.org/simple" }
1613
+ sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" }
1614
+ wheels = [
1615
+ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" },
1616
+ ]