Spaces:
Sleeping
Sleeping
feat: implement golf agent UI with backend integration
Browse files- .gitignore +3 -0
- backend/agents/golf_langgraph.py +25 -1
- backend/api/agent.py +2 -0
- backend/tests/test_course_insights_tool.py +48 -31
- backend/tests/test_golf_langgraph.py +105 -0
- backend/tools/search_golfpedia_tool.py +16 -1
- frontend/index.html +1 -1
- frontend/package-lock.json +9 -9
- frontend/src/App.css +31 -0
- frontend/src/App.jsx +7 -19
- frontend/src/assets/logo_light_transparent.png +0 -0
- frontend/src/components/ChatBox.jsx +68 -28
- frontend/src/components/FileUploader.jsx +0 -75
- pyproject.toml +1 -0
- uv.lock +229 -0
.gitignore
CHANGED
@@ -203,3 +203,6 @@ dist-ssr
|
|
203 |
*.njsproj
|
204 |
*.sln
|
205 |
*.sw?
|
|
|
|
|
|
|
|
203 |
*.njsproj
|
204 |
*.sln
|
205 |
*.sw?
|
206 |
+
|
207 |
+
# Temporary directory exclusion
|
208 |
+
frontend_backup/
|
backend/agents/golf_langgraph.py
CHANGED
@@ -36,6 +36,28 @@ def get_tool_route(state: AgentState) -> str:
|
|
36 |
return "course_insights"
|
37 |
else:
|
38 |
return "search_golfpedia"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
|
40 |
# Router node that simply passes state through
|
41 |
def pass_through_router(state: AgentState) -> AgentState:
|
@@ -76,7 +98,9 @@ for tool_name in tool_map:
|
|
76 |
builder.add_node("summarize", RunnableLambda(summarize_result))
|
77 |
|
78 |
builder.set_entry_point("router")
|
79 |
-
builder.add_conditional_edges("router", get_tool_route)
|
|
|
|
|
80 |
for name in tool_map:
|
81 |
builder.add_edge(name, "summarize")
|
82 |
builder.add_edge("summarize", END)
|
|
|
36 |
return "course_insights"
|
37 |
else:
|
38 |
return "search_golfpedia"
|
39 |
+
|
40 |
+
def route_with_llm(state: AgentState) -> str:
|
41 |
+
query = state.get("input")
|
42 |
+
if not query:
|
43 |
+
raise ValueError("[ROUTER FUNC ERROR] No input found in state.")
|
44 |
+
|
45 |
+
llm = get_llm()
|
46 |
+
response = llm.invoke([
|
47 |
+
HumanMessage(content=f"""Classify this golf-related query into one of the following categories:
|
48 |
+
- "get_pro_stats": if it compares or asks about player stats
|
49 |
+
- "course_insights": if it's asking about a specific golf course
|
50 |
+
- "search_golfpedia": for all other general golf knowledge
|
51 |
+
|
52 |
+
Respond with just one word: get_pro_stats, course_insights, or search_golfpedia.
|
53 |
+
|
54 |
+
Query: "{query}" """)
|
55 |
+
])
|
56 |
+
|
57 |
+
tool_name = response.content.strip()
|
58 |
+
debug_print(f"[ROUTER FUNC w/ LLM] Routed '{query}' → {tool_name}")
|
59 |
+
return tool_name
|
60 |
+
|
61 |
|
62 |
# Router node that simply passes state through
|
63 |
def pass_through_router(state: AgentState) -> AgentState:
|
|
|
98 |
builder.add_node("summarize", RunnableLambda(summarize_result))
|
99 |
|
100 |
builder.set_entry_point("router")
|
101 |
+
# builder.add_conditional_edges("router", get_tool_route)
|
102 |
+
builder.add_conditional_edges("router", route_with_llm)
|
103 |
+
|
104 |
for name in tool_map:
|
105 |
builder.add_edge(name, "summarize")
|
106 |
builder.add_edge("summarize", END)
|
backend/api/agent.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
from fastapi import APIRouter
|
2 |
from pydantic import BaseModel
|
3 |
from backend.agents.golf_langgraph import graph as agent
|
|
|
4 |
|
5 |
router = APIRouter()
|
6 |
|
@@ -47,3 +48,4 @@ async def run_agent(request: AgentRequest):
|
|
47 |
|
48 |
#return {"response": output}
|
49 |
return {"response": result["final_response"]}
|
|
|
|
1 |
from fastapi import APIRouter
|
2 |
from pydantic import BaseModel
|
3 |
from backend.agents.golf_langgraph import graph as agent
|
4 |
+
from backend.tools.utils import debug_print
|
5 |
|
6 |
router = APIRouter()
|
7 |
|
|
|
48 |
|
49 |
#return {"response": output}
|
50 |
return {"response": result["final_response"]}
|
51 |
+
|
backend/tests/test_course_insights_tool.py
CHANGED
@@ -25,18 +25,20 @@ def mock_search_response():
|
|
25 |
@pytest.fixture
|
26 |
def mock_detail_response():
|
27 |
return {
|
28 |
-
"
|
29 |
-
"
|
30 |
-
|
31 |
-
|
32 |
-
"
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
|
|
|
|
40 |
}
|
41 |
}
|
42 |
|
@@ -72,7 +74,7 @@ def test_course_insights_successful(mock_search_response, mock_detail_response,
|
|
72 |
mock_requests_get.side_effect = [search_response, detail_response]
|
73 |
|
74 |
# Call the function
|
75 |
-
result = course_insights.invoke("Pine Valley")
|
76 |
|
77 |
# Verify the result
|
78 |
expected_result = (
|
@@ -88,7 +90,7 @@ def test_course_insights_successful(mock_search_response, mock_detail_response,
|
|
88 |
mock_requests_get.assert_any_call(
|
89 |
"https://api.golfcourseapi.com/v1/search",
|
90 |
headers={"Authorization": "Key fake-api-key"},
|
91 |
-
params={"search_query": "Pine Valley"}
|
92 |
)
|
93 |
mock_requests_get.assert_any_call(
|
94 |
"https://api.golfcourseapi.com/v1/courses/12345",
|
@@ -120,8 +122,10 @@ def test_course_insights_no_tee_data(mock_search_response, mock_requests_get):
|
|
120 |
|
121 |
detail_response = MagicMock()
|
122 |
detail_response.json.return_value = {
|
123 |
-
"
|
124 |
-
|
|
|
|
|
125 |
}
|
126 |
detail_response.raise_for_status.return_value = None
|
127 |
|
@@ -143,17 +147,19 @@ def test_course_insights_female_tee_data(mock_search_response, mock_requests_get
|
|
143 |
|
144 |
detail_response = MagicMock()
|
145 |
detail_response.json.return_value = {
|
146 |
-
"
|
147 |
-
|
148 |
-
"
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
|
|
|
|
157 |
}
|
158 |
}
|
159 |
detail_response.raise_for_status.return_value = None
|
@@ -214,18 +220,29 @@ def test_course_insights_empty_query(query, mock_requests_get):
|
|
214 |
assert result == f"No courses found for query '{query}'."
|
215 |
|
216 |
def test_course_insights_missing_location(mock_search_response, mock_requests_get):
|
|
|
217 |
search_response = MagicMock()
|
218 |
search_response.json.return_value = mock_search_response
|
219 |
search_response.raise_for_status.return_value = None
|
220 |
|
221 |
detail_response = MagicMock()
|
222 |
detail_response.json.return_value = {
|
223 |
-
"
|
224 |
-
|
|
|
|
|
225 |
}
|
226 |
detail_response.raise_for_status.return_value = None
|
227 |
|
228 |
mock_requests_get.side_effect = [search_response, detail_response]
|
229 |
result = course_insights.invoke("Pine Valley")
|
230 |
-
|
231 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
@pytest.fixture
|
26 |
def mock_detail_response():
|
27 |
return {
|
28 |
+
"course": {
|
29 |
+
"location": {
|
30 |
+
"address": "123 Golf Lane, Pine Valley, NJ 08021"
|
31 |
+
},
|
32 |
+
"tees": {
|
33 |
+
"male": [
|
34 |
+
{
|
35 |
+
"course_rating": 74.1,
|
36 |
+
"slope_rating": 155,
|
37 |
+
"total_yards": 7280,
|
38 |
+
"par_total": 72
|
39 |
+
}
|
40 |
+
]
|
41 |
+
}
|
42 |
}
|
43 |
}
|
44 |
|
|
|
74 |
mock_requests_get.side_effect = [search_response, detail_response]
|
75 |
|
76 |
# Call the function
|
77 |
+
result = course_insights.invoke("Pine Valley Course")
|
78 |
|
79 |
# Verify the result
|
80 |
expected_result = (
|
|
|
90 |
mock_requests_get.assert_any_call(
|
91 |
"https://api.golfcourseapi.com/v1/search",
|
92 |
headers={"Authorization": "Key fake-api-key"},
|
93 |
+
params={"search_query": "Pine Valley Course"}
|
94 |
)
|
95 |
mock_requests_get.assert_any_call(
|
96 |
"https://api.golfcourseapi.com/v1/courses/12345",
|
|
|
122 |
|
123 |
detail_response = MagicMock()
|
124 |
detail_response.json.return_value = {
|
125 |
+
"course": {
|
126 |
+
"location": {"address": "123 Golf Lane"},
|
127 |
+
"tees": {"male": [], "female": []}
|
128 |
+
}
|
129 |
}
|
130 |
detail_response.raise_for_status.return_value = None
|
131 |
|
|
|
147 |
|
148 |
detail_response = MagicMock()
|
149 |
detail_response.json.return_value = {
|
150 |
+
"course": {
|
151 |
+
"location": {"address": "123 Golf Lane, Pine Valley, NJ 08021"},
|
152 |
+
"tees": {
|
153 |
+
"male": [],
|
154 |
+
"female": [
|
155 |
+
{
|
156 |
+
"course_rating": 72.5,
|
157 |
+
"slope_rating": 140,
|
158 |
+
"total_yards": 6500,
|
159 |
+
"par_total": 72
|
160 |
+
}
|
161 |
+
]
|
162 |
+
}
|
163 |
}
|
164 |
}
|
165 |
detail_response.raise_for_status.return_value = None
|
|
|
220 |
assert result == f"No courses found for query '{query}'."
|
221 |
|
222 |
def test_course_insights_missing_location(mock_search_response, mock_requests_get):
|
223 |
+
"""Test when location data is missing"""
|
224 |
search_response = MagicMock()
|
225 |
search_response.json.return_value = mock_search_response
|
226 |
search_response.raise_for_status.return_value = None
|
227 |
|
228 |
detail_response = MagicMock()
|
229 |
detail_response.json.return_value = {
|
230 |
+
"course": {
|
231 |
+
"tees": {"male": [{"course_rating": 74.1, "slope_rating": 155, "total_yards": 7280, "par_total": 72}]}
|
232 |
+
# Missing "location"
|
233 |
+
}
|
234 |
}
|
235 |
detail_response.raise_for_status.return_value = None
|
236 |
|
237 |
mock_requests_get.side_effect = [search_response, detail_response]
|
238 |
result = course_insights.invoke("Pine Valley")
|
239 |
+
|
240 |
+
# Verify the result
|
241 |
+
expected_result = (
|
242 |
+
"Pine Valley Golf Club - Championship Course (ID: 12345)\n"
|
243 |
+
"Location: Address not available\n"
|
244 |
+
"Rating: 74.1 | Slope: 155\n"
|
245 |
+
"Yards: 7280 | Par: 72\n"
|
246 |
+
"Hardest Hole: TBD\n"
|
247 |
+
)
|
248 |
+
assert result == expected_result
|
backend/tests/test_golf_langgraph.py
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pytest
|
2 |
+
import asyncio
|
3 |
+
from unittest.mock import patch, MagicMock
|
4 |
+
from backend.agents.golf_langgraph import get_tool_route, AgentState, tool_map
|
5 |
+
|
6 |
+
# Create a mock graph that we can use for testing
|
7 |
+
@pytest.fixture
|
8 |
+
def mock_graph():
|
9 |
+
"""Create a mock graph for testing."""
|
10 |
+
async def mock_ainvoke(input_dict):
|
11 |
+
# Extract the input
|
12 |
+
query = input_dict.get("input", "")
|
13 |
+
|
14 |
+
# Determine which tool to use based on the query
|
15 |
+
tool_name = get_tool_route({"input": query})
|
16 |
+
|
17 |
+
# Return a mock result
|
18 |
+
return {
|
19 |
+
"input": query,
|
20 |
+
"tool_result": f"Mock result for {tool_name}",
|
21 |
+
"final_response": f"This is a mock summary response for {query}"
|
22 |
+
}
|
23 |
+
|
24 |
+
mock = MagicMock()
|
25 |
+
mock.ainvoke = mock_ainvoke
|
26 |
+
return mock
|
27 |
+
|
28 |
+
@pytest.fixture
|
29 |
+
def mock_llm():
|
30 |
+
"""Mock the LLM to avoid actual API calls."""
|
31 |
+
with patch('backend.agents.golf_langgraph.get_llm') as mock:
|
32 |
+
mock_llm = MagicMock()
|
33 |
+
mock_llm.invoke.return_value.content = "This is a mock summary response."
|
34 |
+
mock.return_value = mock_llm
|
35 |
+
yield mock
|
36 |
+
|
37 |
+
@pytest.fixture
|
38 |
+
def mock_tools():
|
39 |
+
"""Mock the tools to avoid actual API calls."""
|
40 |
+
mock_tools = {
|
41 |
+
"get_pro_stats": MagicMock(),
|
42 |
+
"course_insights": MagicMock(),
|
43 |
+
"search_golfpedia": MagicMock()
|
44 |
+
}
|
45 |
+
mock_tools["get_pro_stats"].invoke.return_value = "Mock pro stats result"
|
46 |
+
mock_tools["course_insights"].invoke.return_value = "Mock course insights result"
|
47 |
+
mock_tools["search_golfpedia"].invoke.return_value = "Mock golfpedia search result"
|
48 |
+
return mock_tools
|
49 |
+
|
50 |
+
@pytest.mark.asyncio
|
51 |
+
async def test_graph_pro_stats_query(mock_graph):
|
52 |
+
"""Test the graph with a pro stats query."""
|
53 |
+
# Patch the graph with our mock
|
54 |
+
with patch('backend.agents.golf_langgraph.graph', mock_graph):
|
55 |
+
result = await mock_graph.ainvoke({"input": "Compare Scottie Scheffler and Rory McIlroy in putting"})
|
56 |
+
|
57 |
+
# Check the final response
|
58 |
+
assert result["final_response"] == "This is a mock summary response for Compare Scottie Scheffler and Rory McIlroy in putting"
|
59 |
+
assert result["tool_result"] == "Mock result for get_pro_stats"
|
60 |
+
|
61 |
+
@pytest.mark.asyncio
|
62 |
+
async def test_graph_course_insights_query(mock_graph):
|
63 |
+
"""Test the graph with a course insights query."""
|
64 |
+
# Patch the graph with our mock
|
65 |
+
with patch('backend.agents.golf_langgraph.graph', mock_graph):
|
66 |
+
result = await mock_graph.ainvoke({"input": "What is the course layout at Pine Valley?"})
|
67 |
+
|
68 |
+
# Check the final response
|
69 |
+
assert result["final_response"] == "This is a mock summary response for What is the course layout at Pine Valley?"
|
70 |
+
assert result["tool_result"] == "Mock result for course_insights"
|
71 |
+
|
72 |
+
@pytest.mark.asyncio
|
73 |
+
async def test_graph_search_golfpedia_query(mock_graph):
|
74 |
+
"""Test the graph with a search golfpedia query."""
|
75 |
+
# Patch the graph with our mock
|
76 |
+
with patch('backend.agents.golf_langgraph.graph', mock_graph):
|
77 |
+
result = await mock_graph.ainvoke({"input": "What is the history of golf?"})
|
78 |
+
|
79 |
+
# Check the final response
|
80 |
+
assert result["final_response"] == "This is a mock summary response for What is the history of golf?"
|
81 |
+
assert result["tool_result"] == "Mock result for search_golfpedia"
|
82 |
+
|
83 |
+
def test_get_tool_route():
|
84 |
+
"""Test the tool routing logic."""
|
85 |
+
# Test pro stats route
|
86 |
+
state = AgentState(input="Compare Scottie Scheffler and Rory McIlroy in putting")
|
87 |
+
assert get_tool_route(state) == "get_pro_stats"
|
88 |
+
|
89 |
+
# Test course insights route - update to match current implementation
|
90 |
+
state = AgentState(input="Tell me about Pine Valley Golf Club")
|
91 |
+
# The current implementation doesn't route "Tell me about Pine Valley Golf Club" to course_insights
|
92 |
+
# because it doesn't contain "course" or "yardage" in the query
|
93 |
+
assert get_tool_route(state) == "search_golfpedia"
|
94 |
+
|
95 |
+
# Test course insights route with a query that should match
|
96 |
+
state = AgentState(input="What is the course layout at Pine Valley?")
|
97 |
+
assert get_tool_route(state) == "course_insights"
|
98 |
+
|
99 |
+
# Test search golfpedia route
|
100 |
+
state = AgentState(input="What is the history of golf?")
|
101 |
+
assert get_tool_route(state) == "search_golfpedia"
|
102 |
+
|
103 |
+
# Test error handling
|
104 |
+
with pytest.raises(ValueError):
|
105 |
+
get_tool_route(AgentState())
|
backend/tools/search_golfpedia_tool.py
CHANGED
@@ -16,4 +16,19 @@ def search_golfpedia(query: str) -> str:
|
|
16 |
tavily = get_tavily_client()
|
17 |
result = tavily.search(query=query, search_depth="basic")
|
18 |
debug_print(f"[TOOL RESULT] search_golfpedia: {json.dumps(result, indent=2)}")
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
tavily = get_tavily_client()
|
17 |
result = tavily.search(query=query, search_depth="basic")
|
18 |
debug_print(f"[TOOL RESULT] search_golfpedia: {json.dumps(result, indent=2)}")
|
19 |
+
|
20 |
+
# If there's a direct answer, return it
|
21 |
+
if result.get("answer"):
|
22 |
+
return result["answer"]
|
23 |
+
|
24 |
+
# If there are search results, format them into a readable string
|
25 |
+
if result.get("results") and len(result["results"]) > 0:
|
26 |
+
formatted_results = "Here are some relevant search results:\n\n"
|
27 |
+
for i, item in enumerate(result["results"][:5], 1): # Limit to top 5 results
|
28 |
+
formatted_results += f"{i}. {item.get('title', 'No title')}\n"
|
29 |
+
formatted_results += f" {item.get('content', 'No content')}\n"
|
30 |
+
formatted_results += f" Source: {item.get('url', 'No URL')}\n\n"
|
31 |
+
return formatted_results
|
32 |
+
|
33 |
+
# If no answer and no results, return the default message
|
34 |
+
return NO_SUMMARY_AVAILABLE
|
frontend/index.html
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8" />
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
-
<title>TMD -
|
7 |
</head>
|
8 |
<body>
|
9 |
<div id="root"></div>
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8" />
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
6 |
+
<title>TMD - Golf Caddie AI</title>
|
7 |
</head>
|
8 |
<body>
|
9 |
<div id="root"></div>
|
frontend/package-lock.json
CHANGED
@@ -2586,13 +2586,13 @@
|
|
2586 |
}
|
2587 |
},
|
2588 |
"node_modules/tinyglobby": {
|
2589 |
-
"version": "0.2.
|
2590 |
-
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.
|
2591 |
-
"integrity": "sha512-
|
2592 |
"dev": true,
|
2593 |
"license": "MIT",
|
2594 |
"dependencies": {
|
2595 |
-
"fdir": "^6.4.
|
2596 |
"picomatch": "^4.0.2"
|
2597 |
},
|
2598 |
"engines": {
|
@@ -2657,18 +2657,18 @@
|
|
2657 |
}
|
2658 |
},
|
2659 |
"node_modules/vite": {
|
2660 |
-
"version": "6.3.
|
2661 |
-
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.
|
2662 |
-
"integrity": "sha512-
|
2663 |
"dev": true,
|
2664 |
"license": "MIT",
|
2665 |
"dependencies": {
|
2666 |
"esbuild": "^0.25.0",
|
2667 |
-
"fdir": "^6.4.
|
2668 |
"picomatch": "^4.0.2",
|
2669 |
"postcss": "^8.5.3",
|
2670 |
"rollup": "^4.34.9",
|
2671 |
-
"tinyglobby": "^0.2.
|
2672 |
},
|
2673 |
"bin": {
|
2674 |
"vite": "bin/vite.js"
|
|
|
2586 |
}
|
2587 |
},
|
2588 |
"node_modules/tinyglobby": {
|
2589 |
+
"version": "0.2.13",
|
2590 |
+
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
|
2591 |
+
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
|
2592 |
"dev": true,
|
2593 |
"license": "MIT",
|
2594 |
"dependencies": {
|
2595 |
+
"fdir": "^6.4.4",
|
2596 |
"picomatch": "^4.0.2"
|
2597 |
},
|
2598 |
"engines": {
|
|
|
2657 |
}
|
2658 |
},
|
2659 |
"node_modules/vite": {
|
2660 |
+
"version": "6.3.5",
|
2661 |
+
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
|
2662 |
+
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
|
2663 |
"dev": true,
|
2664 |
"license": "MIT",
|
2665 |
"dependencies": {
|
2666 |
"esbuild": "^0.25.0",
|
2667 |
+
"fdir": "^6.4.4",
|
2668 |
"picomatch": "^4.0.2",
|
2669 |
"postcss": "^8.5.3",
|
2670 |
"rollup": "^4.34.9",
|
2671 |
+
"tinyglobby": "^0.2.13"
|
2672 |
},
|
2673 |
"bin": {
|
2674 |
"vite": "bin/vite.js"
|
frontend/src/App.css
CHANGED
@@ -123,3 +123,34 @@
|
|
123 |
.question-input-row button:hover:not(:disabled) {
|
124 |
background-color: var(--primary-dark);
|
125 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
.question-input-row button:hover:not(:disabled) {
|
124 |
background-color: var(--primary-dark);
|
125 |
}
|
126 |
+
|
127 |
+
.response-panel {
|
128 |
+
flex: 1;
|
129 |
+
overflow-y: auto;
|
130 |
+
padding: 1rem;
|
131 |
+
background: #f5f5f5;
|
132 |
+
border-radius: 8px;
|
133 |
+
margin-bottom: 1rem;
|
134 |
+
min-height: 200px;
|
135 |
+
max-height: 60vh;
|
136 |
+
}
|
137 |
+
|
138 |
+
.message {
|
139 |
+
margin-bottom: 1rem;
|
140 |
+
padding: 0.75rem;
|
141 |
+
border-radius: 8px;
|
142 |
+
max-width: 80%;
|
143 |
+
word-wrap: break-word;
|
144 |
+
}
|
145 |
+
|
146 |
+
.user-message {
|
147 |
+
background-color: #007bff;
|
148 |
+
color: white;
|
149 |
+
margin-left: auto;
|
150 |
+
}
|
151 |
+
|
152 |
+
.ai-message {
|
153 |
+
background-color: #e9ecef;
|
154 |
+
color: #212529;
|
155 |
+
margin-right: auto;
|
156 |
+
}
|
frontend/src/App.jsx
CHANGED
@@ -1,33 +1,21 @@
|
|
1 |
-
import FileUploader from './components/FileUploader'
|
2 |
import ChatBox from './components/ChatBox'
|
3 |
-
import { useState } from 'react'
|
4 |
import './App.css' // Layout styles
|
|
|
5 |
|
6 |
function App() {
|
7 |
-
const [fileUploaded, setFileUploaded] = useState(false)
|
8 |
-
|
9 |
return (
|
10 |
-
<div className=
|
11 |
<div className="branding">
|
12 |
-
{/* TODO: Replace branding logo and app title with your own */}
|
13 |
<img
|
14 |
-
src=
|
15 |
-
alt="
|
16 |
className="logo"
|
17 |
/>
|
18 |
-
<h1>
|
19 |
</div>
|
20 |
|
21 |
-
|
22 |
-
<
|
23 |
-
{/* TODO: Customize ChatBox behavior, styling, or replace it entirely */}
|
24 |
-
<ChatBox />
|
25 |
-
</div>
|
26 |
-
)}
|
27 |
-
|
28 |
-
<div className="footer-uploader">
|
29 |
-
{/* TODO: Replace FileUploader or bypass if not needed */}
|
30 |
-
<FileUploader onUploadSuccess={() => setFileUploaded(true)} />
|
31 |
</div>
|
32 |
</div>
|
33 |
)
|
|
|
|
|
1 |
import ChatBox from './components/ChatBox'
|
|
|
2 |
import './App.css' // Layout styles
|
3 |
+
import logo from './assets/logo_light_transparent.png'
|
4 |
|
5 |
function App() {
|
|
|
|
|
6 |
return (
|
7 |
+
<div className="app-root chat-mode">
|
8 |
<div className="branding">
|
|
|
9 |
<img
|
10 |
+
src={logo}
|
11 |
+
alt="Golf Caddie Logo"
|
12 |
className="logo"
|
13 |
/>
|
14 |
+
<h1>Golf Caddie AI</h1>
|
15 |
</div>
|
16 |
|
17 |
+
<div className="chat-section">
|
18 |
+
<ChatBox />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
</div>
|
20 |
</div>
|
21 |
)
|
frontend/src/assets/logo_light_transparent.png
ADDED
![]() |
frontend/src/components/ChatBox.jsx
CHANGED
@@ -1,39 +1,49 @@
|
|
1 |
-
import { useState } from 'react'
|
2 |
import { getApiUrl } from '../utils/env'
|
3 |
|
4 |
export default function ChatBox() {
|
5 |
const [question, setQuestion] = useState('')
|
6 |
-
const [
|
7 |
const [isStreaming, setIsStreaming] = useState(false)
|
8 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
|
10 |
const askQuestion = async (e) => {
|
11 |
e.preventDefault()
|
12 |
if (!question.trim()) return
|
13 |
|
|
|
|
|
|
|
|
|
14 |
setIsStreaming(true)
|
15 |
-
setResponse('')
|
16 |
|
17 |
try {
|
18 |
-
const
|
19 |
-
formData.append(endpoint === '/ask' ? 'question' : 'query', question) // flexible
|
20 |
-
|
21 |
-
const res = await fetch(`${getApiUrl()}${endpoint}`, {
|
22 |
method: 'POST',
|
23 |
-
|
|
|
|
|
|
|
24 |
})
|
25 |
|
26 |
if (!res.ok) {
|
27 |
-
// Try to parse the error response
|
28 |
try {
|
29 |
const errorData = await res.json()
|
30 |
throw new Error(errorData.error || `HTTP error! status: ${res.status}`)
|
31 |
} catch (e) {
|
32 |
-
// If parsing fails, use the status text
|
33 |
throw new Error(`HTTP error! status: ${res.status}`)
|
34 |
}
|
35 |
}
|
36 |
|
|
|
37 |
if (res.body) {
|
38 |
const reader = res.body.getReader()
|
39 |
const decoder = new TextDecoder('utf-8')
|
@@ -42,29 +52,67 @@ export default function ChatBox() {
|
|
42 |
if (done) break
|
43 |
const text = decoder.decode(value)
|
44 |
try {
|
45 |
-
const parsed = JSON.parse(text)
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
} catch {
|
48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
}
|
51 |
} else {
|
52 |
const data = await res.json()
|
53 |
-
|
|
|
54 |
}
|
55 |
} catch (error) {
|
56 |
console.error('Error:', error)
|
57 |
-
|
58 |
} finally {
|
59 |
setIsStreaming(false)
|
60 |
}
|
61 |
}
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
return (
|
64 |
<>
|
65 |
-
{/* TODO: Customize this UI based on your agent or query-based backend */}
|
66 |
<div className="response-panel" data-testid="response-panel">
|
67 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
</div>
|
69 |
|
70 |
<div className="question-input-row">
|
@@ -73,18 +121,10 @@ export default function ChatBox() {
|
|
73 |
rows={3}
|
74 |
value={question}
|
75 |
onChange={(e) => setQuestion(e.target.value)}
|
76 |
-
|
|
|
77 |
/>
|
78 |
|
79 |
-
<select
|
80 |
-
value={endpoint}
|
81 |
-
onChange={(e) => setEndpoint(e.target.value)}
|
82 |
-
disabled={isStreaming}
|
83 |
-
>
|
84 |
-
<option value="/ask">RAG (/ask)</option>
|
85 |
-
<option value="/agent">Agent (/agent)</option>
|
86 |
-
</select>
|
87 |
-
|
88 |
<button onClick={askQuestion} disabled={isStreaming}>
|
89 |
{isStreaming ? 'Thinking…' : 'Ask'}
|
90 |
</button>
|
|
|
1 |
+
import { useState, useRef, useEffect } from 'react'
|
2 |
import { getApiUrl } from '../utils/env'
|
3 |
|
4 |
export default function ChatBox() {
|
5 |
const [question, setQuestion] = useState('')
|
6 |
+
const [messages, setMessages] = useState([])
|
7 |
const [isStreaming, setIsStreaming] = useState(false)
|
8 |
+
const chatEndRef = useRef(null)
|
9 |
+
|
10 |
+
const scrollToBottom = () => {
|
11 |
+
chatEndRef.current?.scrollIntoView({ behavior: "smooth" })
|
12 |
+
}
|
13 |
+
|
14 |
+
useEffect(() => {
|
15 |
+
scrollToBottom()
|
16 |
+
}, [messages])
|
17 |
|
18 |
const askQuestion = async (e) => {
|
19 |
e.preventDefault()
|
20 |
if (!question.trim()) return
|
21 |
|
22 |
+
// Add user message to chat
|
23 |
+
const userMessage = { type: 'user', content: question }
|
24 |
+
setMessages(prev => [...prev, userMessage])
|
25 |
+
setQuestion('') // Clear the input
|
26 |
setIsStreaming(true)
|
|
|
27 |
|
28 |
try {
|
29 |
+
const res = await fetch(`${getApiUrl()}/agent`, {
|
|
|
|
|
|
|
30 |
method: 'POST',
|
31 |
+
headers: {
|
32 |
+
'Content-Type': 'application/json',
|
33 |
+
},
|
34 |
+
body: JSON.stringify({ query: question }),
|
35 |
})
|
36 |
|
37 |
if (!res.ok) {
|
|
|
38 |
try {
|
39 |
const errorData = await res.json()
|
40 |
throw new Error(errorData.error || `HTTP error! status: ${res.status}`)
|
41 |
} catch (e) {
|
|
|
42 |
throw new Error(`HTTP error! status: ${res.status}`)
|
43 |
}
|
44 |
}
|
45 |
|
46 |
+
let aiResponse = ''
|
47 |
if (res.body) {
|
48 |
const reader = res.body.getReader()
|
49 |
const decoder = new TextDecoder('utf-8')
|
|
|
52 |
if (done) break
|
53 |
const text = decoder.decode(value)
|
54 |
try {
|
55 |
+
const parsed = JSON.parse(text)
|
56 |
+
const responseText = parsed.response || parsed.final_response || text
|
57 |
+
aiResponse += responseText
|
58 |
+
setMessages(prev => {
|
59 |
+
const newMessages = [...prev]
|
60 |
+
if (newMessages[newMessages.length - 1]?.type === 'ai') {
|
61 |
+
newMessages[newMessages.length - 1].content = aiResponse
|
62 |
+
} else {
|
63 |
+
newMessages.push({ type: 'ai', content: aiResponse })
|
64 |
+
}
|
65 |
+
return newMessages
|
66 |
+
})
|
67 |
} catch {
|
68 |
+
aiResponse += text
|
69 |
+
setMessages(prev => {
|
70 |
+
const newMessages = [...prev]
|
71 |
+
if (newMessages[newMessages.length - 1]?.type === 'ai') {
|
72 |
+
newMessages[newMessages.length - 1].content = aiResponse
|
73 |
+
} else {
|
74 |
+
newMessages.push({ type: 'ai', content: aiResponse })
|
75 |
+
}
|
76 |
+
return newMessages
|
77 |
+
})
|
78 |
}
|
79 |
}
|
80 |
} else {
|
81 |
const data = await res.json()
|
82 |
+
const responseText = data.response || data.final_response || 'No response received'
|
83 |
+
setMessages(prev => [...prev, { type: 'ai', content: responseText }])
|
84 |
}
|
85 |
} catch (error) {
|
86 |
console.error('Error:', error)
|
87 |
+
setMessages(prev => [...prev, { type: 'ai', content: 'Error: ' + error.message }])
|
88 |
} finally {
|
89 |
setIsStreaming(false)
|
90 |
}
|
91 |
}
|
92 |
|
93 |
+
const handleKeyDown = (e) => {
|
94 |
+
if (e.key === 'Enter' && !e.shiftKey) {
|
95 |
+
e.preventDefault()
|
96 |
+
askQuestion(e)
|
97 |
+
}
|
98 |
+
}
|
99 |
+
|
100 |
return (
|
101 |
<>
|
|
|
102 |
<div className="response-panel" data-testid="response-panel">
|
103 |
+
{messages.length === 0 ? (
|
104 |
+
'Ask me anything about golf...'
|
105 |
+
) : (
|
106 |
+
messages.map((message, index) => (
|
107 |
+
<div
|
108 |
+
key={index}
|
109 |
+
className={`message ${message.type === 'user' ? 'user-message' : 'ai-message'}`}
|
110 |
+
>
|
111 |
+
{message.content}
|
112 |
+
</div>
|
113 |
+
))
|
114 |
+
)}
|
115 |
+
<div ref={chatEndRef} />
|
116 |
</div>
|
117 |
|
118 |
<div className="question-input-row">
|
|
|
121 |
rows={3}
|
122 |
value={question}
|
123 |
onChange={(e) => setQuestion(e.target.value)}
|
124 |
+
onKeyDown={handleKeyDown}
|
125 |
+
placeholder="Ask a question about golf... (Press Enter to submit, Shift+Enter for new line)"
|
126 |
/>
|
127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
<button onClick={askQuestion} disabled={isStreaming}>
|
129 |
{isStreaming ? 'Thinking…' : 'Ask'}
|
130 |
</button>
|
frontend/src/components/FileUploader.jsx
DELETED
@@ -1,75 +0,0 @@
|
|
1 |
-
import { useState } from 'react'
|
2 |
-
import { getApiUrl } from '../utils/env'
|
3 |
-
|
4 |
-
function FileUploader({ onUploadSuccess }) {
|
5 |
-
const [file, setFile] = useState(null)
|
6 |
-
const [uploading, setUploading] = useState(false)
|
7 |
-
const [message, setMessage] = useState('')
|
8 |
-
|
9 |
-
const handleFileChange = (e) => {
|
10 |
-
setFile(e.target.files[0])
|
11 |
-
setMessage('')
|
12 |
-
}
|
13 |
-
|
14 |
-
const uploadFile = async (e) => {
|
15 |
-
e.preventDefault()
|
16 |
-
if (!file) {
|
17 |
-
setMessage('Please select a file first')
|
18 |
-
return
|
19 |
-
}
|
20 |
-
|
21 |
-
setUploading(true)
|
22 |
-
setMessage('')
|
23 |
-
|
24 |
-
const formData = new FormData()
|
25 |
-
formData.append('file', file)
|
26 |
-
|
27 |
-
try {
|
28 |
-
// TODO: This endpoint expects a backend route POST /upload
|
29 |
-
const response = await fetch(`${getApiUrl()}/upload`, {
|
30 |
-
method: 'POST',
|
31 |
-
body: formData,
|
32 |
-
})
|
33 |
-
|
34 |
-
if (!response.ok) {
|
35 |
-
throw new Error(`Upload failed with status: ${response.status}`)
|
36 |
-
}
|
37 |
-
|
38 |
-
const data = await response.json()
|
39 |
-
setMessage(data.message || 'Upload successful!')
|
40 |
-
setFile(null)
|
41 |
-
if (onUploadSuccess) onUploadSuccess()
|
42 |
-
} catch (error) {
|
43 |
-
console.error('Upload error:', error)
|
44 |
-
setMessage('Upload error: ' + error.message)
|
45 |
-
} finally {
|
46 |
-
setUploading(false)
|
47 |
-
}
|
48 |
-
}
|
49 |
-
|
50 |
-
return (
|
51 |
-
<div className="file-uploader">
|
52 |
-
{/* TODO: Replace or style this component for your use case */}
|
53 |
-
<form onSubmit={uploadFile} role="form">
|
54 |
-
<label htmlFor="file-input">File:</label>
|
55 |
-
<input
|
56 |
-
id="file-input"
|
57 |
-
type="file"
|
58 |
-
onChange={handleFileChange}
|
59 |
-
disabled={uploading}
|
60 |
-
/>
|
61 |
-
<button type="submit" disabled={!file || uploading}>
|
62 |
-
{uploading ? 'Uploading...' : 'Upload'}
|
63 |
-
</button>
|
64 |
-
</form>
|
65 |
-
|
66 |
-
{message && (
|
67 |
-
<p className="message" data-testid="upload-message">
|
68 |
-
{message}
|
69 |
-
</p>
|
70 |
-
)}
|
71 |
-
</div>
|
72 |
-
)
|
73 |
-
}
|
74 |
-
|
75 |
-
export default FileUploader
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pyproject.toml
CHANGED
@@ -16,6 +16,7 @@ dependencies = [
|
|
16 |
"langchain-community>=0.0.22",
|
17 |
"langchain-openai>=0.0.8",
|
18 |
"pytest>=7.0.0", # Added pytest dependency
|
|
|
19 |
"tavily-python>=0.3.0", # Added tavily dependency
|
20 |
"langgraph>=0.0.15", # Added langgraph dependency
|
21 |
]
|
|
|
16 |
"langchain-community>=0.0.22",
|
17 |
"langchain-openai>=0.0.8",
|
18 |
"pytest>=7.0.0", # Added pytest dependency
|
19 |
+
"pytest-asyncio>=0.23.0", # Added pytest-asyncio dependency
|
20 |
"tavily-python>=0.3.0", # Added tavily dependency
|
21 |
"langgraph>=0.0.15", # Added langgraph dependency
|
22 |
]
|
uv.lock
CHANGED
@@ -845,6 +845,61 @@ wheels = [
|
|
845 |
{ url = "https://files.pythonhosted.org/packages/8b/a3/3696ff2444658053c01b6b7443e761f28bb71217d82bb89137a978c5f66f/langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02", size = 32440 },
|
846 |
]
|
847 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
848 |
[[package]]
|
849 |
name = "langsmith"
|
850 |
version = "0.3.32"
|
@@ -872,14 +927,17 @@ dependencies = [
|
|
872 |
{ name = "langchain" },
|
873 |
{ name = "langchain-community" },
|
874 |
{ name = "langchain-openai" },
|
|
|
875 |
{ name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
876 |
{ name = "numpy", version = "2.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
877 |
{ name = "openai" },
|
878 |
{ name = "pypdf" },
|
879 |
{ name = "pypdf2" },
|
880 |
{ name = "pytest" },
|
|
|
881 |
{ name = "python-dotenv" },
|
882 |
{ name = "python-multipart" },
|
|
|
883 |
{ name = "uvicorn", extra = ["standard"] },
|
884 |
]
|
885 |
|
@@ -889,13 +947,16 @@ requires-dist = [
|
|
889 |
{ name = "langchain", specifier = ">=0.1.0" },
|
890 |
{ name = "langchain-community", specifier = ">=0.0.22" },
|
891 |
{ name = "langchain-openai", specifier = ">=0.0.8" },
|
|
|
892 |
{ name = "numpy", specifier = ">=1.24.0" },
|
893 |
{ name = "openai", specifier = ">=1.0.0" },
|
894 |
{ name = "pypdf" },
|
895 |
{ name = "pypdf2", specifier = ">=3.0.0" },
|
896 |
{ name = "pytest", specifier = ">=7.0.0" },
|
|
|
897 |
{ name = "python-dotenv" },
|
898 |
{ name = "python-multipart" },
|
|
|
899 |
{ name = "uvicorn", extras = ["standard"] },
|
900 |
]
|
901 |
|
@@ -1249,6 +1310,54 @@ wheels = [
|
|
1249 |
{ url = "https://files.pythonhosted.org/packages/d7/78/8db408b16d0cf53a3e9d195bd2866759a7dcd5a89a28e3c9d3c8b8f85649/orjson-3.10.16-cp39-cp39-win_amd64.whl", hash = "sha256:c338dc2296d1ed0d5c5c27dfb22d00b330555cb706c2e0be1e1c3940a0895905", size = 133598 },
|
1250 |
]
|
1251 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1252 |
[[package]]
|
1253 |
name = "packaging"
|
1254 |
version = "24.2"
|
@@ -1560,6 +1669,19 @@ wheels = [
|
|
1560 |
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
|
1561 |
]
|
1562 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1563 |
[[package]]
|
1564 |
name = "python-dotenv"
|
1565 |
version = "1.1.0"
|
@@ -1818,6 +1940,20 @@ wheels = [
|
|
1818 |
{ url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 },
|
1819 |
]
|
1820 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1821 |
[[package]]
|
1822 |
name = "tenacity"
|
1823 |
version = "9.1.2"
|
@@ -2183,6 +2319,99 @@ wheels = [
|
|
2183 |
{ url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 },
|
2184 |
]
|
2185 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2186 |
[[package]]
|
2187 |
name = "yarl"
|
2188 |
version = "1.20.0"
|
|
|
845 |
{ url = "https://files.pythonhosted.org/packages/8b/a3/3696ff2444658053c01b6b7443e761f28bb71217d82bb89137a978c5f66f/langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02", size = 32440 },
|
846 |
]
|
847 |
|
848 |
+
[[package]]
|
849 |
+
name = "langgraph"
|
850 |
+
version = "0.3.31"
|
851 |
+
source = { registry = "https://pypi.org/simple" }
|
852 |
+
dependencies = [
|
853 |
+
{ name = "langchain-core" },
|
854 |
+
{ name = "langgraph-checkpoint" },
|
855 |
+
{ name = "langgraph-prebuilt" },
|
856 |
+
{ name = "langgraph-sdk" },
|
857 |
+
{ name = "xxhash" },
|
858 |
+
]
|
859 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b5/49/f6814bc1b016fd8f403856e986a4a0b35fd9cc34671f2abf649753643194/langgraph-0.3.31.tar.gz", hash = "sha256:c76bbc8f26604929d6c2520d937b5602d5bb1dde2c0580398d015f7f6841f14c", size = 120379 }
|
860 |
+
wheels = [
|
861 |
+
{ url = "https://files.pythonhosted.org/packages/53/0f/c4802c26da60b84af1ee4a1d3013378da2354220aa2d0e766d07c5db1de2/langgraph-0.3.31-py3-none-any.whl", hash = "sha256:f42a6850d03696f2a54d3c833db39aa201cfda78217846a5b8936ad95fe41e6c", size = 145197 },
|
862 |
+
]
|
863 |
+
|
864 |
+
[[package]]
|
865 |
+
name = "langgraph-checkpoint"
|
866 |
+
version = "2.0.24"
|
867 |
+
source = { registry = "https://pypi.org/simple" }
|
868 |
+
dependencies = [
|
869 |
+
{ name = "langchain-core" },
|
870 |
+
{ name = "ormsgpack" },
|
871 |
+
]
|
872 |
+
sdist = { url = "https://files.pythonhosted.org/packages/0d/df/bacef68562ba4c391ded751eecda8e579ec78a581506064cf625e0ebd93a/langgraph_checkpoint-2.0.24.tar.gz", hash = "sha256:9596dad332344e7e871257be464df8a07c2e9bac66143081b11b9422b0167e5b", size = 37328 }
|
873 |
+
wheels = [
|
874 |
+
{ url = "https://files.pythonhosted.org/packages/bc/60/30397e8fd2b7dead3754aa79d708caff9dbb371f30b4cd21802c60f6b921/langgraph_checkpoint-2.0.24-py3-none-any.whl", hash = "sha256:3836e2909ef2387d1fa8d04ee3e2a353f980d519fd6c649af352676dc73d66b8", size = 42028 },
|
875 |
+
]
|
876 |
+
|
877 |
+
[[package]]
|
878 |
+
name = "langgraph-prebuilt"
|
879 |
+
version = "0.1.8"
|
880 |
+
source = { registry = "https://pypi.org/simple" }
|
881 |
+
dependencies = [
|
882 |
+
{ name = "langchain-core" },
|
883 |
+
{ name = "langgraph-checkpoint" },
|
884 |
+
]
|
885 |
+
sdist = { url = "https://files.pythonhosted.org/packages/57/30/f31f0e076c37d097b53e4cff5d479a3686e1991f6c86a1a4727d5d1f5489/langgraph_prebuilt-0.1.8.tar.gz", hash = "sha256:4de7659151829b2b955b6798df6800e580e617782c15c2c5b29b139697491831", size = 24543 }
|
886 |
+
wheels = [
|
887 |
+
{ url = "https://files.pythonhosted.org/packages/36/72/9e092665502f8f52f2708065ed14fbbba3f95d1a1b65d62049b0c5fcdf00/langgraph_prebuilt-0.1.8-py3-none-any.whl", hash = "sha256:ae97b828ae00be2cefec503423aa782e1bff165e9b94592e224da132f2526968", size = 25903 },
|
888 |
+
]
|
889 |
+
|
890 |
+
[[package]]
|
891 |
+
name = "langgraph-sdk"
|
892 |
+
version = "0.1.63"
|
893 |
+
source = { registry = "https://pypi.org/simple" }
|
894 |
+
dependencies = [
|
895 |
+
{ name = "httpx" },
|
896 |
+
{ name = "orjson" },
|
897 |
+
]
|
898 |
+
sdist = { url = "https://files.pythonhosted.org/packages/38/bc/23571ccda8bd442b7090e5f5263fbae439dcfbe7c06c2d51b55d464dfb69/langgraph_sdk-0.1.63.tar.gz", hash = "sha256:62bf2cc31e5aa6c5b9011ee1702bcf1e36e67e142a60bd97af2611162fb58e18", size = 43837 }
|
899 |
+
wheels = [
|
900 |
+
{ url = "https://files.pythonhosted.org/packages/5a/1f/f1462fef67ddca45b2e4dd1cfbb452aa4b6532edaf77b99f9588406229b0/langgraph_sdk-0.1.63-py3-none-any.whl", hash = "sha256:6fb78a7fc6a30eea43bd0d6401dbc9e3263d0d4c03f63c04035980da7e586b05", size = 47343 },
|
901 |
+
]
|
902 |
+
|
903 |
[[package]]
|
904 |
name = "langsmith"
|
905 |
version = "0.3.32"
|
|
|
927 |
{ name = "langchain" },
|
928 |
{ name = "langchain-community" },
|
929 |
{ name = "langchain-openai" },
|
930 |
+
{ name = "langgraph" },
|
931 |
{ name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" },
|
932 |
{ name = "numpy", version = "2.2.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" },
|
933 |
{ name = "openai" },
|
934 |
{ name = "pypdf" },
|
935 |
{ name = "pypdf2" },
|
936 |
{ name = "pytest" },
|
937 |
+
{ name = "pytest-asyncio" },
|
938 |
{ name = "python-dotenv" },
|
939 |
{ name = "python-multipart" },
|
940 |
+
{ name = "tavily-python" },
|
941 |
{ name = "uvicorn", extra = ["standard"] },
|
942 |
]
|
943 |
|
|
|
947 |
{ name = "langchain", specifier = ">=0.1.0" },
|
948 |
{ name = "langchain-community", specifier = ">=0.0.22" },
|
949 |
{ name = "langchain-openai", specifier = ">=0.0.8" },
|
950 |
+
{ name = "langgraph", specifier = ">=0.0.15" },
|
951 |
{ name = "numpy", specifier = ">=1.24.0" },
|
952 |
{ name = "openai", specifier = ">=1.0.0" },
|
953 |
{ name = "pypdf" },
|
954 |
{ name = "pypdf2", specifier = ">=3.0.0" },
|
955 |
{ name = "pytest", specifier = ">=7.0.0" },
|
956 |
+
{ name = "pytest-asyncio", specifier = ">=0.23.0" },
|
957 |
{ name = "python-dotenv" },
|
958 |
{ name = "python-multipart" },
|
959 |
+
{ name = "tavily-python", specifier = ">=0.3.0" },
|
960 |
{ name = "uvicorn", extras = ["standard"] },
|
961 |
]
|
962 |
|
|
|
1310 |
{ url = "https://files.pythonhosted.org/packages/d7/78/8db408b16d0cf53a3e9d195bd2866759a7dcd5a89a28e3c9d3c8b8f85649/orjson-3.10.16-cp39-cp39-win_amd64.whl", hash = "sha256:c338dc2296d1ed0d5c5c27dfb22d00b330555cb706c2e0be1e1c3940a0895905", size = 133598 },
|
1311 |
]
|
1312 |
|
1313 |
+
[[package]]
|
1314 |
+
name = "ormsgpack"
|
1315 |
+
version = "1.9.1"
|
1316 |
+
source = { registry = "https://pypi.org/simple" }
|
1317 |
+
sdist = { url = "https://files.pythonhosted.org/packages/25/a7/462cf8ff5e29241868b82d3a5ec124d690eb6a6a5c6fa5bb1367b839e027/ormsgpack-1.9.1.tar.gz", hash = "sha256:3da6e63d82565e590b98178545e64f0f8506137b92bd31a2d04fd7c82baf5794", size = 56887 }
|
1318 |
+
wheels = [
|
1319 |
+
{ url = "https://files.pythonhosted.org/packages/7f/32/5f504c0695ff96aaaf0452bee522d79b5a3ee809f22fd77fdb0dd5756d86/ormsgpack-1.9.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:f1f804fd9c0fd84213a6022c34172f82323b34afa7052a4af18797582cf56365", size = 382793 },
|
1320 |
+
{ url = "https://files.pythonhosted.org/packages/1e/c6/64fe1270271b61495611f1d3068baedb57d76e0f93ce7156f3763fb79b32/ormsgpack-1.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eab5cec99c46276b37071d570aab98603f3d0309b3818da3247eb64bb95e5cfc", size = 213974 },
|
1321 |
+
{ url = "https://files.pythonhosted.org/packages/13/56/6666d6a9b82c7d2021fce6823ff823bc373a4e7280979c1b453317678fbc/ormsgpack-1.9.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c12c6bb30e6df6fc0213b77f0a5e143f371d618be2e8eb4d555340ce01c6900", size = 217200 },
|
1322 |
+
{ url = "https://files.pythonhosted.org/packages/5f/fb/b844ed1e69d8615163525a8403d7abd3548b3fbfa0f3a973808f36145a0f/ormsgpack-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:994d4bbb7ee333264a3e55e30ccee063df6635d785f21a08bf52f67821454a51", size = 223648 },
|
1323 |
+
{ url = "https://files.pythonhosted.org/packages/f2/26/c40c3e300f9c61a5ed7a6921656dd0d2907a8174936e1e643677585e497c/ormsgpack-1.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a668a584cf4bb6e1a6ef5a35f3f0d0fdae80cfb7237344ad19a50cce8c79317b", size = 394197 },
|
1324 |
+
{ url = "https://files.pythonhosted.org/packages/2d/4c/7a4ae187f18e7abf7ab0662b473264a60a5aa4e9bff266f541a8855df163/ormsgpack-1.9.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:aaf77699203822638014c604d100f132583844d4fd01eb639a2266970c02cfdf", size = 480550 },
|
1325 |
+
{ url = "https://files.pythonhosted.org/packages/b1/33/5c465dfd5571f816835bb9e371987bf081b529c64ef28a72d18b0b59902d/ormsgpack-1.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:003d7e1992b447898caf25a820b3037ec68a57864b3e2f34b64693b7d60a9984", size = 396955 },
|
1326 |
+
{ url = "https://files.pythonhosted.org/packages/8f/fd/8f64f477b5c6d66e9c6343d7d3f32d7063ba20ab151dd36884e6504899ab/ormsgpack-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:67fefc77e4ba9469f79426769eb4c78acf21f22bef3ab1239a72dd728036ffc2", size = 125102 },
|
1327 |
+
{ url = "https://files.pythonhosted.org/packages/d8/3b/388e7915a28db6ab3daedfd4937bd7b063c50dd1543068daa31c0a3b70ed/ormsgpack-1.9.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:16eaf32c33ab4249e242181d59e2509b8e0330d6f65c1d8bf08c3dea38fd7c02", size = 382794 },
|
1328 |
+
{ url = "https://files.pythonhosted.org/packages/0f/b4/3f4afba058822bf69b274e0defe507056be0340e65363c3ebcd312b01b84/ormsgpack-1.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c70f2e5b2f9975536e8f7936a9721601dc54febe363d2d82f74c9b31d4fe1c65", size = 213974 },
|
1329 |
+
{ url = "https://files.pythonhosted.org/packages/bf/be/f0e21366d51b6e28fc3a55425be6a125545370d3479bf25be081e83ee236/ormsgpack-1.9.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:17c9e18b07d69e3db2e0f8af4731040175e11bdfde78ad8e28126e9e66ec5167", size = 217200 },
|
1330 |
+
{ url = "https://files.pythonhosted.org/packages/cc/90/67a23c1c880a6e5552acb45f9555b642528f89c8bcf75283a2ea64ef7175/ormsgpack-1.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73538d749096bb6470328601a2be8f7bdec28849ec6fd19595c232a5848d7124", size = 223649 },
|
1331 |
+
{ url = "https://files.pythonhosted.org/packages/80/ad/116c1f970b5b4453e4faa52645517a2e5eaf1ab385ba09a5c54253d07d0e/ormsgpack-1.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:827ff71de228cfd6d07b9d6b47911aa61b1e8dc995dec3caf8fdcdf4f874bcd0", size = 394200 },
|
1332 |
+
{ url = "https://files.pythonhosted.org/packages/c5/a2/b224a5ef193628a15205e473179276b87e8290d321693e4934a05cbd6ccf/ormsgpack-1.9.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:7307f808b3df282c8e8ed92c6ebceeb3eea3d8eeec808438f3f212226b25e217", size = 480551 },
|
1333 |
+
{ url = "https://files.pythonhosted.org/packages/b5/f4/a0f528196af6ab46e6c3f3051cf7403016bdc7b7d3e673ea5b04b145be98/ormsgpack-1.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f30aad7fb083bed1c540a3c163c6a9f63a94e3c538860bf8f13386c29b560ad5", size = 396959 },
|
1334 |
+
{ url = "https://files.pythonhosted.org/packages/bc/6b/60c6f4787e3e93f5eb34fccb163753a8771465983a579e3405152f2422fd/ormsgpack-1.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:829a1b4c5bc3c38ece0c55cf91ebc09c3b987fceb24d3f680c2bcd03fd3789a4", size = 125100 },
|
1335 |
+
{ url = "https://files.pythonhosted.org/packages/dd/f1/155a598cc8030526ccaaf91ba4d61530f87900645559487edba58b0a90a2/ormsgpack-1.9.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:1ede445fc3fdba219bb0e0d1f289df26a9c7602016b7daac6fafe8fe4e91548f", size = 383225 },
|
1336 |
+
{ url = "https://files.pythonhosted.org/packages/23/1c/ef3097ba550fad55c79525f461febdd4e0d9cc18d065248044536f09488e/ormsgpack-1.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db50b9f918e25b289114312ed775794d0978b469831b992bdc65bfe20b91fe30", size = 214056 },
|
1337 |
+
{ url = "https://files.pythonhosted.org/packages/27/77/64d0da25896b2cbb99505ca518c109d7dd1964d7fde14c10943731738b60/ormsgpack-1.9.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8c7d8fc58e4333308f58ec720b1ee6b12b2b3fe2d2d8f0766ab751cb351e8757", size = 217339 },
|
1338 |
+
{ url = "https://files.pythonhosted.org/packages/6c/10/c3a7fd0a0068b0bb52cccbfeb5656db895d69e895a3abbc210c4b3f98ff8/ormsgpack-1.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeee6d08c040db265cb8563444aba343ecb32cbdbe2414a489dcead9f70c6765", size = 223816 },
|
1339 |
+
{ url = "https://files.pythonhosted.org/packages/43/e7/aee1238dba652f2116c2523d36fd1c5f9775436032be5c233108fd2a1415/ormsgpack-1.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2fbb8181c198bdc413a4e889e5200f010724eea4b6d5a9a7eee2df039ac04aca", size = 394287 },
|
1340 |
+
{ url = "https://files.pythonhosted.org/packages/c7/09/1b452a92376f29d7a2da7c18fb01cf09978197a8eccbb8b204e72fd5a970/ormsgpack-1.9.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:16488f094ac0e2250cceea6caf72962614aa432ee11dd57ef45e1ad25ece3eff", size = 480709 },
|
1341 |
+
{ url = "https://files.pythonhosted.org/packages/de/13/7fa9fee5a73af8a73a42bf8c2e69489605714f65f5a41454400a05e84a3b/ormsgpack-1.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:422d960bfd6ad88be20794f50ec7953d8f7a0f2df60e19d0e8feb994e2ed64ee", size = 397247 },
|
1342 |
+
{ url = "https://files.pythonhosted.org/packages/a1/2d/2e87cb28110db0d3bb750edd4d8719b5068852a2eef5e96b0bf376bb8a81/ormsgpack-1.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:e6e2f9eab527cf43fb4a4293e493370276b1c8716cf305689202d646c6a782ef", size = 125368 },
|
1343 |
+
{ url = "https://files.pythonhosted.org/packages/b8/54/0390d5d092831e4df29dbafe32402891fc14b3e6ffe5a644b16cbbc9d9bc/ormsgpack-1.9.1-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:ac61c18d9dd085e8519b949f7e655f7fb07909fd09c53b4338dd33309012e289", size = 383226 },
|
1344 |
+
{ url = "https://files.pythonhosted.org/packages/47/64/8b15d262d1caefead8fb22ec144f5ff7d9505fc31c22bc34598053d46fbe/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134840b8c6615da2c24ce77bd12a46098015c808197a9995c7a2d991e1904eec", size = 214057 },
|
1345 |
+
{ url = "https://files.pythonhosted.org/packages/57/00/65823609266bad4d5ed29ea753d24a3bdb01c7edaf923da80967fc31f9c5/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38fd42618f626394b2c7713c5d4bcbc917254e9753d5d4cde460658b51b11a74", size = 217340 },
|
1346 |
+
{ url = "https://files.pythonhosted.org/packages/a0/51/e535c50f7f87b49110233647f55300d7975139ef5e51f1adb4c55f58c124/ormsgpack-1.9.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d36397333ad07b9eba4c2e271fa78951bd81afc059c85a6e9f6c0eb2de07cda", size = 223815 },
|
1347 |
+
{ url = "https://files.pythonhosted.org/packages/0c/ee/393e4a6de2a62124bf589602648f295a9fb3907a0e2fe80061b88899d072/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:603063089597917d04e4c1b1d53988a34f7dc2ff1a03adcfd1cf4ae966d5fba6", size = 394287 },
|
1348 |
+
{ url = "https://files.pythonhosted.org/packages/c6/d8/e56d7c3cb73a0e533e3e2a21ae5838b2aa36a9dac1ca9c861af6bae5a369/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:94bbf2b185e0cb721ceaba20e64b7158e6caf0cecd140ca29b9f05a8d5e91e2f", size = 480707 },
|
1349 |
+
{ url = "https://files.pythonhosted.org/packages/e6/e0/6a3c6a6dc98583a721c54b02f5195bde8f801aebdeda9b601fa2ab30ad39/ormsgpack-1.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38f380b1e8c96a712eb302b9349347385161a8e29046868ae2bfdfcb23e2692", size = 397246 },
|
1350 |
+
{ url = "https://files.pythonhosted.org/packages/b0/60/0ee5d790f13507e1f75ac21fc82dc1ef29afe1f520bd0f249d65b2f4839b/ormsgpack-1.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:a4bc63fb30db94075611cedbbc3d261dd17cf2aa8ff75a0fd684cd45ca29cb1b", size = 125371 },
|
1351 |
+
{ url = "https://files.pythonhosted.org/packages/85/02/ac4a2263c9aad0d455f240e1bdd41b443e5452257cf13bc188177b0dfd1f/ormsgpack-1.9.1-cp39-cp39-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e95909248bece8e88a310a913838f17ff5a39190aa4e61de909c3cd27f59744b", size = 382789 },
|
1352 |
+
{ url = "https://files.pythonhosted.org/packages/81/6f/e50d070ae3a6aa7cb50849d0796ac6d72c0f8f01d5a42438c9567ab352e3/ormsgpack-1.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3939188810c5c641d6b207f29994142ae2b1c70534f7839bbd972d857ac2072", size = 213967 },
|
1353 |
+
{ url = "https://files.pythonhosted.org/packages/55/1d/379734bca4f2d71ce11c7096d85276280cf13d1bb7243bf809b171c25cda/ormsgpack-1.9.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b6476344a585aea00a2acc9fd07355bf2daac04062cfdd480fa83ec3e2403b", size = 217183 },
|
1354 |
+
{ url = "https://files.pythonhosted.org/packages/9a/f6/036a44ada8659b1729db5f20ba50dc1945a84a50cd4fa6b3a74d0f16fab9/ormsgpack-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d8b9d53da82b31662ce5a3834b65479cf794a34befb9fc50baa51518383250", size = 223647 },
|
1355 |
+
{ url = "https://files.pythonhosted.org/packages/b9/61/5c8671ab3b7cac21076169972bad9f4faa1fdda1f70e0ae78b365894b164/ormsgpack-1.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3933d4b0c0d404ee234dbc372836d6f2d2f4b6330c2a2fb9709ba4eaebfae7ba", size = 394232 },
|
1356 |
+
{ url = "https://files.pythonhosted.org/packages/7b/70/6e9ba8c8c405dee5dffd86edf1188ef1a116421598e18df89dac0c499aae/ormsgpack-1.9.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:f824e94a7969f0aee9a6847ec232cf731a03b8734951c2a774dd4762308ea2d2", size = 480582 },
|
1357 |
+
{ url = "https://files.pythonhosted.org/packages/a6/e1/2113022dd9236cea13f0ba850a5f8a640c0c9e3b07bfbbaf01409190068b/ormsgpack-1.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c1f3f2295374020f9650e4aa7af6403ff016a0d92778b4a48bb3901fd801232d", size = 396952 },
|
1358 |
+
{ url = "https://files.pythonhosted.org/packages/01/d4/58ca5de3124ac975dae1a96a475c3cb9ed70c70dfba39fd4ceca53838ee6/ormsgpack-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:92eb1b4f7b168da47f547329b4b58d16d8f19508a97ce5266567385d42d81968", size = 125107 },
|
1359 |
+
]
|
1360 |
+
|
1361 |
[[package]]
|
1362 |
name = "packaging"
|
1363 |
version = "24.2"
|
|
|
1669 |
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
|
1670 |
]
|
1671 |
|
1672 |
+
[[package]]
|
1673 |
+
name = "pytest-asyncio"
|
1674 |
+
version = "0.26.0"
|
1675 |
+
source = { registry = "https://pypi.org/simple" }
|
1676 |
+
dependencies = [
|
1677 |
+
{ name = "pytest" },
|
1678 |
+
{ name = "typing-extensions", marker = "python_full_version < '3.10'" },
|
1679 |
+
]
|
1680 |
+
sdist = { url = "https://files.pythonhosted.org/packages/8e/c4/453c52c659521066969523e87d85d54139bbd17b78f09532fb8eb8cdb58e/pytest_asyncio-0.26.0.tar.gz", hash = "sha256:c4df2a697648241ff39e7f0e4a73050b03f123f760673956cf0d72a4990e312f", size = 54156 }
|
1681 |
+
wheels = [
|
1682 |
+
{ url = "https://files.pythonhosted.org/packages/20/7f/338843f449ace853647ace35870874f69a764d251872ed1b4de9f234822c/pytest_asyncio-0.26.0-py3-none-any.whl", hash = "sha256:7b51ed894f4fbea1340262bdae5135797ebbe21d8638978e35d31c6d19f72fb0", size = 19694 },
|
1683 |
+
]
|
1684 |
+
|
1685 |
[[package]]
|
1686 |
name = "python-dotenv"
|
1687 |
version = "1.1.0"
|
|
|
1940 |
{ url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 },
|
1941 |
]
|
1942 |
|
1943 |
+
[[package]]
|
1944 |
+
name = "tavily-python"
|
1945 |
+
version = "0.5.4"
|
1946 |
+
source = { registry = "https://pypi.org/simple" }
|
1947 |
+
dependencies = [
|
1948 |
+
{ name = "httpx" },
|
1949 |
+
{ name = "requests" },
|
1950 |
+
{ name = "tiktoken" },
|
1951 |
+
]
|
1952 |
+
sdist = { url = "https://files.pythonhosted.org/packages/2c/72/f36a49c2fd344b45e6f1ddab59a7f809bff4f46dcc78af7ac9758461a5bf/tavily_python-0.5.4.tar.gz", hash = "sha256:fdad5303f9f6603a06fddcc7e21b128bebc1adf7694e553a664caf87eb2d2d9d", size = 109050 }
|
1953 |
+
wheels = [
|
1954 |
+
{ url = "https://files.pythonhosted.org/packages/2f/dd/63d1d2fddcaaee040745ea58d13b54c0b1483260ddae52eb59abf691f757/tavily_python-0.5.4-py3-none-any.whl", hash = "sha256:47f8c0b41283d44849fe9531596cd26d3de42a59618ef66f9e1244d8fedba404", size = 44419 },
|
1955 |
+
]
|
1956 |
+
|
1957 |
[[package]]
|
1958 |
name = "tenacity"
|
1959 |
version = "9.1.2"
|
|
|
2319 |
{ url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 },
|
2320 |
]
|
2321 |
|
2322 |
+
[[package]]
|
2323 |
+
name = "xxhash"
|
2324 |
+
version = "3.5.0"
|
2325 |
+
source = { registry = "https://pypi.org/simple" }
|
2326 |
+
sdist = { url = "https://files.pythonhosted.org/packages/00/5e/d6e5258d69df8b4ed8c83b6664f2b47d30d2dec551a29ad72a6c69eafd31/xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f", size = 84241 }
|
2327 |
+
wheels = [
|
2328 |
+
{ url = "https://files.pythonhosted.org/packages/bb/8a/0e9feca390d512d293afd844d31670e25608c4a901e10202aa98785eab09/xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212", size = 31970 },
|
2329 |
+
{ url = "https://files.pythonhosted.org/packages/16/e6/be5aa49580cd064a18200ab78e29b88b1127e1a8c7955eb8ecf81f2626eb/xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520", size = 30801 },
|
2330 |
+
{ url = "https://files.pythonhosted.org/packages/20/ee/b8a99ebbc6d1113b3a3f09e747fa318c3cde5b04bd9c197688fadf0eeae8/xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680", size = 220927 },
|
2331 |
+
{ url = "https://files.pythonhosted.org/packages/58/62/15d10582ef159283a5c2b47f6d799fc3303fe3911d5bb0bcc820e1ef7ff4/xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da", size = 200360 },
|
2332 |
+
{ url = "https://files.pythonhosted.org/packages/23/41/61202663ea9b1bd8e53673b8ec9e2619989353dba8cfb68e59a9cbd9ffe3/xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23", size = 428528 },
|
2333 |
+
{ url = "https://files.pythonhosted.org/packages/f2/07/d9a3059f702dec5b3b703737afb6dda32f304f6e9da181a229dafd052c29/xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196", size = 194149 },
|
2334 |
+
{ url = "https://files.pythonhosted.org/packages/eb/58/27caadf78226ecf1d62dbd0c01d152ed381c14c1ee4ad01f0d460fc40eac/xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c", size = 207703 },
|
2335 |
+
{ url = "https://files.pythonhosted.org/packages/b1/08/32d558ce23e1e068453c39aed7b3c1cdc690c177873ec0ca3a90d5808765/xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482", size = 216255 },
|
2336 |
+
{ url = "https://files.pythonhosted.org/packages/3f/d4/2b971e2d2b0a61045f842b622ef11e94096cf1f12cd448b6fd426e80e0e2/xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296", size = 202744 },
|
2337 |
+
{ url = "https://files.pythonhosted.org/packages/19/ae/6a6438864a8c4c39915d7b65effd85392ebe22710412902487e51769146d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415", size = 210115 },
|
2338 |
+
{ url = "https://files.pythonhosted.org/packages/48/7d/b3c27c27d1fc868094d02fe4498ccce8cec9fcc591825c01d6bcb0b4fc49/xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198", size = 414247 },
|
2339 |
+
{ url = "https://files.pythonhosted.org/packages/a1/05/918f9e7d2fbbd334b829997045d341d6239b563c44e683b9a7ef8fe50f5d/xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442", size = 191419 },
|
2340 |
+
{ url = "https://files.pythonhosted.org/packages/08/29/dfe393805b2f86bfc47c290b275f0b7c189dc2f4e136fd4754f32eb18a8d/xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da", size = 30114 },
|
2341 |
+
{ url = "https://files.pythonhosted.org/packages/7b/d7/aa0b22c4ebb7c3ccb993d4c565132abc641cd11164f8952d89eb6a501909/xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9", size = 30003 },
|
2342 |
+
{ url = "https://files.pythonhosted.org/packages/69/12/f969b81541ee91b55f1ce469d7ab55079593c80d04fd01691b550e535000/xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6", size = 26773 },
|
2343 |
+
{ url = "https://files.pythonhosted.org/packages/b8/c7/afed0f131fbda960ff15eee7f304fa0eeb2d58770fade99897984852ef23/xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1", size = 31969 },
|
2344 |
+
{ url = "https://files.pythonhosted.org/packages/8c/0c/7c3bc6d87e5235672fcc2fb42fd5ad79fe1033925f71bf549ee068c7d1ca/xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8", size = 30800 },
|
2345 |
+
{ url = "https://files.pythonhosted.org/packages/04/9e/01067981d98069eec1c20201f8c145367698e9056f8bc295346e4ea32dd1/xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166", size = 221566 },
|
2346 |
+
{ url = "https://files.pythonhosted.org/packages/d4/09/d4996de4059c3ce5342b6e1e6a77c9d6c91acce31f6ed979891872dd162b/xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7", size = 201214 },
|
2347 |
+
{ url = "https://files.pythonhosted.org/packages/62/f5/6d2dc9f8d55a7ce0f5e7bfef916e67536f01b85d32a9fbf137d4cadbee38/xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623", size = 429433 },
|
2348 |
+
{ url = "https://files.pythonhosted.org/packages/d9/72/9256303f10e41ab004799a4aa74b80b3c5977d6383ae4550548b24bd1971/xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a", size = 194822 },
|
2349 |
+
{ url = "https://files.pythonhosted.org/packages/34/92/1a3a29acd08248a34b0e6a94f4e0ed9b8379a4ff471f1668e4dce7bdbaa8/xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88", size = 208538 },
|
2350 |
+
{ url = "https://files.pythonhosted.org/packages/53/ad/7fa1a109663366de42f724a1cdb8e796a260dbac45047bce153bc1e18abf/xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c", size = 216953 },
|
2351 |
+
{ url = "https://files.pythonhosted.org/packages/35/02/137300e24203bf2b2a49b48ce898ecce6fd01789c0fcd9c686c0a002d129/xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2", size = 203594 },
|
2352 |
+
{ url = "https://files.pythonhosted.org/packages/23/03/aeceb273933d7eee248c4322b98b8e971f06cc3880e5f7602c94e5578af5/xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084", size = 210971 },
|
2353 |
+
{ url = "https://files.pythonhosted.org/packages/e3/64/ed82ec09489474cbb35c716b189ddc1521d8b3de12b1b5ab41ce7f70253c/xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d", size = 415050 },
|
2354 |
+
{ url = "https://files.pythonhosted.org/packages/71/43/6db4c02dcb488ad4e03bc86d70506c3d40a384ee73c9b5c93338eb1f3c23/xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839", size = 192216 },
|
2355 |
+
{ url = "https://files.pythonhosted.org/packages/22/6d/db4abec29e7a567455344433d095fdb39c97db6955bb4a2c432e486b4d28/xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da", size = 30120 },
|
2356 |
+
{ url = "https://files.pythonhosted.org/packages/52/1c/fa3b61c0cf03e1da4767213672efe186b1dfa4fc901a4a694fb184a513d1/xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58", size = 30003 },
|
2357 |
+
{ url = "https://files.pythonhosted.org/packages/6b/8e/9e6fc572acf6e1cc7ccb01973c213f895cb8668a9d4c2b58a99350da14b7/xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3", size = 26777 },
|
2358 |
+
{ url = "https://files.pythonhosted.org/packages/07/0e/1bfce2502c57d7e2e787600b31c83535af83746885aa1a5f153d8c8059d6/xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00", size = 31969 },
|
2359 |
+
{ url = "https://files.pythonhosted.org/packages/3f/d6/8ca450d6fe5b71ce521b4e5db69622383d039e2b253e9b2f24f93265b52c/xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9", size = 30787 },
|
2360 |
+
{ url = "https://files.pythonhosted.org/packages/5b/84/de7c89bc6ef63d750159086a6ada6416cc4349eab23f76ab870407178b93/xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84", size = 220959 },
|
2361 |
+
{ url = "https://files.pythonhosted.org/packages/fe/86/51258d3e8a8545ff26468c977101964c14d56a8a37f5835bc0082426c672/xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793", size = 200006 },
|
2362 |
+
{ url = "https://files.pythonhosted.org/packages/02/0a/96973bd325412feccf23cf3680fd2246aebf4b789122f938d5557c54a6b2/xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be", size = 428326 },
|
2363 |
+
{ url = "https://files.pythonhosted.org/packages/11/a7/81dba5010f7e733de88af9555725146fc133be97ce36533867f4c7e75066/xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6", size = 194380 },
|
2364 |
+
{ url = "https://files.pythonhosted.org/packages/fb/7d/f29006ab398a173f4501c0e4977ba288f1c621d878ec217b4ff516810c04/xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90", size = 207934 },
|
2365 |
+
{ url = "https://files.pythonhosted.org/packages/8a/6e/6e88b8f24612510e73d4d70d9b0c7dff62a2e78451b9f0d042a5462c8d03/xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27", size = 216301 },
|
2366 |
+
{ url = "https://files.pythonhosted.org/packages/af/51/7862f4fa4b75a25c3b4163c8a873f070532fe5f2d3f9b3fc869c8337a398/xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2", size = 203351 },
|
2367 |
+
{ url = "https://files.pythonhosted.org/packages/22/61/8d6a40f288f791cf79ed5bb113159abf0c81d6efb86e734334f698eb4c59/xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d", size = 210294 },
|
2368 |
+
{ url = "https://files.pythonhosted.org/packages/17/02/215c4698955762d45a8158117190261b2dbefe9ae7e5b906768c09d8bc74/xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab", size = 414674 },
|
2369 |
+
{ url = "https://files.pythonhosted.org/packages/31/5c/b7a8db8a3237cff3d535261325d95de509f6a8ae439a5a7a4ffcff478189/xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e", size = 192022 },
|
2370 |
+
{ url = "https://files.pythonhosted.org/packages/78/e3/dd76659b2811b3fd06892a8beb850e1996b63e9235af5a86ea348f053e9e/xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8", size = 30170 },
|
2371 |
+
{ url = "https://files.pythonhosted.org/packages/d9/6b/1c443fe6cfeb4ad1dcf231cdec96eb94fb43d6498b4469ed8b51f8b59a37/xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e", size = 30040 },
|
2372 |
+
{ url = "https://files.pythonhosted.org/packages/0f/eb/04405305f290173acc0350eba6d2f1a794b57925df0398861a20fbafa415/xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2", size = 26796 },
|
2373 |
+
{ url = "https://files.pythonhosted.org/packages/c9/b8/e4b3ad92d249be5c83fa72916c9091b0965cb0faeff05d9a0a3870ae6bff/xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6", size = 31795 },
|
2374 |
+
{ url = "https://files.pythonhosted.org/packages/fc/d8/b3627a0aebfbfa4c12a41e22af3742cf08c8ea84f5cc3367b5de2d039cce/xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5", size = 30792 },
|
2375 |
+
{ url = "https://files.pythonhosted.org/packages/c3/cc/762312960691da989c7cd0545cb120ba2a4148741c6ba458aa723c00a3f8/xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc", size = 220950 },
|
2376 |
+
{ url = "https://files.pythonhosted.org/packages/fe/e9/cc266f1042c3c13750e86a535496b58beb12bf8c50a915c336136f6168dc/xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3", size = 199980 },
|
2377 |
+
{ url = "https://files.pythonhosted.org/packages/bf/85/a836cd0dc5cc20376de26b346858d0ac9656f8f730998ca4324921a010b9/xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c", size = 428324 },
|
2378 |
+
{ url = "https://files.pythonhosted.org/packages/b4/0e/15c243775342ce840b9ba34aceace06a1148fa1630cd8ca269e3223987f5/xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb", size = 194370 },
|
2379 |
+
{ url = "https://files.pythonhosted.org/packages/87/a1/b028bb02636dfdc190da01951d0703b3d904301ed0ef6094d948983bef0e/xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f", size = 207911 },
|
2380 |
+
{ url = "https://files.pythonhosted.org/packages/80/d5/73c73b03fc0ac73dacf069fdf6036c9abad82de0a47549e9912c955ab449/xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7", size = 216352 },
|
2381 |
+
{ url = "https://files.pythonhosted.org/packages/b6/2a/5043dba5ddbe35b4fe6ea0a111280ad9c3d4ba477dd0f2d1fe1129bda9d0/xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326", size = 203410 },
|
2382 |
+
{ url = "https://files.pythonhosted.org/packages/a2/b2/9a8ded888b7b190aed75b484eb5c853ddd48aa2896e7b59bbfbce442f0a1/xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf", size = 210322 },
|
2383 |
+
{ url = "https://files.pythonhosted.org/packages/98/62/440083fafbc917bf3e4b67c2ade621920dd905517e85631c10aac955c1d2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7", size = 414725 },
|
2384 |
+
{ url = "https://files.pythonhosted.org/packages/75/db/009206f7076ad60a517e016bb0058381d96a007ce3f79fa91d3010f49cc2/xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c", size = 192070 },
|
2385 |
+
{ url = "https://files.pythonhosted.org/packages/1f/6d/c61e0668943a034abc3a569cdc5aeae37d686d9da7e39cf2ed621d533e36/xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637", size = 30172 },
|
2386 |
+
{ url = "https://files.pythonhosted.org/packages/96/14/8416dce965f35e3d24722cdf79361ae154fa23e2ab730e5323aa98d7919e/xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43", size = 30041 },
|
2387 |
+
{ url = "https://files.pythonhosted.org/packages/27/ee/518b72faa2073f5aa8e3262408d284892cb79cf2754ba0c3a5870645ef73/xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b", size = 26801 },
|
2388 |
+
{ url = "https://files.pythonhosted.org/packages/d4/f6/531dd6858adf8877675270b9d6989b6dacfd1c2d7135b17584fc29866df3/xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301", size = 31971 },
|
2389 |
+
{ url = "https://files.pythonhosted.org/packages/7c/a8/b2a42b6c9ae46e233f474f3d307c2e7bca8d9817650babeca048d2ad01d6/xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab", size = 30801 },
|
2390 |
+
{ url = "https://files.pythonhosted.org/packages/b4/92/9ac297e3487818f429bcf369c1c6a097edf5b56ed6fc1feff4c1882e87ef/xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f", size = 220644 },
|
2391 |
+
{ url = "https://files.pythonhosted.org/packages/86/48/c1426dd3c86fc4a52f983301867463472f6a9013fb32d15991e60c9919b6/xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd", size = 200021 },
|
2392 |
+
{ url = "https://files.pythonhosted.org/packages/f3/de/0ab8c79993765c94fc0d0c1a22b454483c58a0161e1b562f58b654f47660/xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc", size = 428217 },
|
2393 |
+
{ url = "https://files.pythonhosted.org/packages/b4/b4/332647451ed7d2c021294b7c1e9c144dbb5586b1fb214ad4f5a404642835/xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754", size = 193868 },
|
2394 |
+
{ url = "https://files.pythonhosted.org/packages/f4/1c/a42c0a6cac752f84f7b44a90d1a9fa9047cf70bdba5198a304fde7cc471f/xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6", size = 207403 },
|
2395 |
+
{ url = "https://files.pythonhosted.org/packages/c4/d7/04e1b0daae9dc9b02c73c1664cc8aa527498c3f66ccbc586eeb25bbe9f14/xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898", size = 215978 },
|
2396 |
+
{ url = "https://files.pythonhosted.org/packages/c4/f4/05e15e67505228fc19ee98a79e427b3a0b9695f5567cd66ced5d66389883/xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833", size = 202416 },
|
2397 |
+
{ url = "https://files.pythonhosted.org/packages/94/fb/e9028d3645bba5412a09de13ee36df276a567e60bdb31d499dafa46d76ae/xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6", size = 209853 },
|
2398 |
+
{ url = "https://files.pythonhosted.org/packages/02/2c/18c6a622429368274739372d2f86c8125413ec169025c7d8ffb051784bba/xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af", size = 413926 },
|
2399 |
+
{ url = "https://files.pythonhosted.org/packages/72/bb/5b55c391084a0321c3809632a018b9b657e59d5966289664f85a645942ac/xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606", size = 191156 },
|
2400 |
+
{ url = "https://files.pythonhosted.org/packages/86/2b/915049db13401792fec159f57e4f4a5ca7a9768e83ef71d6645b9d0cd749/xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4", size = 30122 },
|
2401 |
+
{ url = "https://files.pythonhosted.org/packages/d5/87/382ef7b24917d7cf4c540ee30f29b283bc87ac5893d2f89b23ea3cdf7d77/xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558", size = 30021 },
|
2402 |
+
{ url = "https://files.pythonhosted.org/packages/e2/47/d06b24e2d9c3dcabccfd734d11b5bbebfdf59ceac2c61509d8205dd20ac6/xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e", size = 26780 },
|
2403 |
+
{ url = "https://files.pythonhosted.org/packages/ab/9a/233606bada5bd6f50b2b72c45de3d9868ad551e83893d2ac86dc7bb8553a/xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c", size = 29732 },
|
2404 |
+
{ url = "https://files.pythonhosted.org/packages/0c/67/f75276ca39e2c6604e3bee6c84e9db8a56a4973fde9bf35989787cf6e8aa/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986", size = 36214 },
|
2405 |
+
{ url = "https://files.pythonhosted.org/packages/0f/f8/f6c61fd794229cc3848d144f73754a0c107854372d7261419dcbbd286299/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6", size = 32020 },
|
2406 |
+
{ url = "https://files.pythonhosted.org/packages/79/d3/c029c99801526f859e6b38d34ab87c08993bf3dcea34b11275775001638a/xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b", size = 40515 },
|
2407 |
+
{ url = "https://files.pythonhosted.org/packages/62/e3/bef7b82c1997579c94de9ac5ea7626d01ae5858aa22bf4fcb38bf220cb3e/xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da", size = 30064 },
|
2408 |
+
{ url = "https://files.pythonhosted.org/packages/c2/56/30d3df421814947f9d782b20c9b7e5e957f3791cbd89874578011daafcbd/xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9", size = 29734 },
|
2409 |
+
{ url = "https://files.pythonhosted.org/packages/82/dd/3c42a1f022ad0d82c852d3cb65493ebac03dcfa8c994465a5fb052b00e3c/xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1", size = 36216 },
|
2410 |
+
{ url = "https://files.pythonhosted.org/packages/b2/40/8f902ab3bebda228a9b4de69eba988280285a7f7f167b942bc20bb562df9/xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f", size = 32042 },
|
2411 |
+
{ url = "https://files.pythonhosted.org/packages/db/87/bd06beb8ccaa0e9e577c9b909a49cfa5c5cd2ca46034342d72dd9ce5bc56/xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0", size = 40516 },
|
2412 |
+
{ url = "https://files.pythonhosted.org/packages/bb/f8/505385e2fbd753ddcaafd5550eabe86f6232cbebabad3b2508d411b19153/xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240", size = 30108 },
|
2413 |
+
]
|
2414 |
+
|
2415 |
[[package]]
|
2416 |
name = "yarl"
|
2417 |
version = "1.20.0"
|