Geevarghese George commited on
Commit
c5964f7
·
1 Parent(s): a4eaa0c

flow established

Browse files
app.py CHANGED
@@ -1,106 +1,66 @@
1
- import json
2
  import logging
3
 
4
  import gradio as gr
5
  from mcp import StdioServerParameters
6
  from smolagents import InferenceClientModel, MCPClient
7
 
 
 
 
 
 
 
8
  from config import GITHUB_PAT as GITHUB_TOKEN
9
- from config import GITHUB_TOOLSETS, HF_TOKEN
10
  from src.upgrade_advisor.agents.package import PackageDiscoveryAgent
11
- from src.upgrade_advisor.chat.chat import qn_rewriter, run_document_qa
 
 
 
 
12
 
13
  logger = logging.getLogger(__name__)
14
  logger.setLevel(logging.INFO)
15
  logger.addHandler(logging.StreamHandler())
16
 
17
 
18
- def build_context_from_events(events):
19
- # Build a compact textual context from structured tool outputs
20
- lines = []
21
- max_field_len = 500
22
- for e in events:
23
- # Only consider tool outputs here
24
- if hasattr(e, "tool_call") and hasattr(e, "output"):
25
- tool_call = e.tool_call
26
- tool_name = getattr(tool_call, "name", None) if tool_call else None
27
- out = getattr(e, "output", None)
28
- # Convert Pydantic -> dict; else keep dict/list; else str
29
- if hasattr(out, "model_dump"):
30
- data = out.model_dump()
31
- elif isinstance(out, (dict, list)):
32
- data = out
33
- else:
34
- # best-effort parse
35
- try:
36
- data = json.loads(str(out))
37
- except Exception:
38
- data = {"text": str(out)}
39
-
40
- # Redact long fields
41
- def shorten(v):
42
- if isinstance(v, str) and len(v) > max_field_len:
43
- return v[:max_field_len] + "..."
44
- return v
45
-
46
- def safe_kv_pairs(obj, prefix=None):
47
- pairs = []
48
- if isinstance(obj, dict):
49
- for k, v in obj.items():
50
- if isinstance(v, (dict, list)):
51
- pairs.extend(safe_kv_pairs(v, prefix=f"{k}."))
52
- else:
53
- pairs.append((f"{prefix or ''}{k}", shorten(v)))
54
- elif isinstance(obj, list):
55
- for i, v in enumerate(obj[:20]):
56
- if not isinstance(v, (dict, list)):
57
- pairs.append((f"item[{i}]", shorten(v)))
58
- return pairs
59
-
60
- kv = safe_kv_pairs(data)
61
- # Keep only key facts likely useful for QA
62
- key_lines = []
63
- for k, v in kv:
64
- kl = k.lower()
65
- if any(
66
- s in kl
67
- for s in [
68
- "name",
69
- "version",
70
- "summary",
71
- "home",
72
- "project_urls",
73
- "url",
74
- "owner",
75
- "repo",
76
- "releases",
77
- ]
78
- ):
79
- key_lines.append(f"{k}: {v}")
80
- if key_lines:
81
- lines.append(f"[tool:{tool_name}]\n" + "\n".join(key_lines))
82
- return "\n".join(lines)[:8000] # cap context length
83
-
84
-
85
  async def chat_fn(message, history):
 
 
 
 
 
 
 
 
 
 
86
  message = message.strip()
87
- rewritten_qn = await qn_rewriter(message)
88
- logger.info(f"Rewritten question: {rewritten_qn}")
 
 
 
 
 
 
89
  # Collect events from the agent run
90
- events = list(package_agent.discover_package_info(rewritten_qn))
 
 
91
  # Build a concise context from tool outputs
92
- context = build_context_from_events(events)
 
 
 
93
  # Run a document QA pass using the user's question
94
- qa_answer = await run_document_qa(rewritten_qn, context, original_question=message)
95
- # Also append a short bullet summary of key facts
96
- lines = [qa_answer]
97
- if context:
98
- # Extract a few top lines as quick facts
99
- facts = [ln for ln in context.splitlines() if ":" in ln][:10]
100
- if facts:
101
- lines.append("\nKey facts:")
102
- lines.extend(f"- {f}" for f in facts[:10])
103
- return "\n".join(lines)
104
 
105
 
106
  if __name__ == "__main__":
@@ -136,12 +96,8 @@ if __name__ == "__main__":
136
  )
137
 
138
  pypi_mcp_client = MCPClient(
139
- server_parameters=[pypi_mcp_params], structured_output=True
140
- )
141
- gh_mcp_client = MCPClient(
142
- server_parameters=[gh_mcp_params],
143
- structured_output=False, # explicitly set to silence
144
- # FutureWarning; set True if you need structured outputs
145
  )
146
 
147
  with pypi_mcp_client as pypi_toolset:
@@ -156,6 +112,12 @@ if __name__ == "__main__":
156
  fn=chat_fn,
157
  title="Python Package Discovery Agent",
158
  type="messages",
 
 
 
 
 
 
159
  )
160
  demo.launch()
161
 
 
 
1
  import logging
2
 
3
  import gradio as gr
4
  from mcp import StdioServerParameters
5
  from smolagents import InferenceClientModel, MCPClient
6
 
7
+ from config import (
8
+ CHAT_HISTORY_TURNS_CUTOFF,
9
+ CHAT_HISTORY_WORD_CUTOFF,
10
+ GITHUB_TOOLSETS,
11
+ HF_TOKEN,
12
+ )
13
  from config import GITHUB_PAT as GITHUB_TOKEN
 
14
  from src.upgrade_advisor.agents.package import PackageDiscoveryAgent
15
+ from src.upgrade_advisor.chat.chat import (
16
+ qn_rewriter,
17
+ run_document_qa,
18
+ summarize_chat_history,
19
+ )
20
 
21
  logger = logging.getLogger(__name__)
22
  logger.setLevel(logging.INFO)
23
  logger.addHandler(logging.StreamHandler())
24
 
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  async def chat_fn(message, history):
27
+ # parse incoming history is a list of dicts with 'role' and 'content' keys
28
+ if len(history) > 0:
29
+ summarized_history = await summarize_chat_history(
30
+ history,
31
+ turns_cutoff=CHAT_HISTORY_TURNS_CUTOFF,
32
+ word_cutoff=CHAT_HISTORY_WORD_CUTOFF,
33
+ )
34
+ else:
35
+ summarized_history = ""
36
+
37
  message = message.strip()
38
+ rewritten_message, is_rewritten_good = await qn_rewriter(
39
+ message, summarized_history
40
+ )
41
+ if is_rewritten_good:
42
+ logger.info(f"Rewritten question: {rewritten_message}")
43
+ else:
44
+ logger.info(f"Using original question: {message}")
45
+ rewritten_message = None
46
  # Collect events from the agent run
47
+ events = package_agent.discover_package_info(
48
+ user_input=message, reframed_question=rewritten_message
49
+ )
50
  # Build a concise context from tool outputs
51
+ # context = build_context_from_events(events)
52
+ context = events
53
+ logger.info(f"Built context of length {len(context)}")
54
+ logger.info(f"Context content:\n{context}")
55
  # Run a document QA pass using the user's question
56
+ qa_answer = await run_document_qa(
57
+ question=message, context=context, rewritten_question=rewritten_message
58
+ )
59
+ logger.info(f"QA answer: {qa_answer}")
60
+ return {
61
+ "role": "assistant",
62
+ "content": qa_answer,
63
+ }
 
 
64
 
65
 
66
  if __name__ == "__main__":
 
96
  )
97
 
98
  pypi_mcp_client = MCPClient(
99
+ server_parameters=[pypi_mcp_params, gh_mcp_params],
100
+ structured_output=True,
 
 
 
 
101
  )
102
 
103
  with pypi_mcp_client as pypi_toolset:
 
112
  fn=chat_fn,
113
  title="Python Package Discovery Agent",
114
  type="messages",
115
+ save_history=True,
116
+ examples=[
117
+ ["Tell me about the 'requests' package."],
118
+ ["What is the latest version of 'numpy'?"],
119
+ ["Which version of 'pandas' is compatible with 'numpy' 2.0?"],
120
+ ],
121
  )
122
  demo.launch()
123
 
config.py CHANGED
@@ -20,3 +20,6 @@ GITHUB_TOOLSETS = os.getenv("GITHUB_TOOLSETS", "repos")
20
  GRADIO_SERVER_NAME = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
21
  GRADIO_SERVER_PORT = os.getenv("GRADIO_SERVER_PORT", "7860")
22
  GRADIO_SHARE = os.getenv("GRADIO_SHARE", "False")
 
 
 
 
20
  GRADIO_SERVER_NAME = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
21
  GRADIO_SERVER_PORT = os.getenv("GRADIO_SERVER_PORT", "7860")
22
  GRADIO_SHARE = os.getenv("GRADIO_SHARE", "False")
23
+
24
+ CHAT_HISTORY_TURNS_CUTOFF = int(os.getenv("CHAT_HISTORY_TURNS_CUTOFF", "10"))
25
+ CHAT_HISTORY_WORD_CUTOFF = int(os.getenv("CHAT_HISTORY_WORD_CUTOFF", "100"))
poetry.lock CHANGED
@@ -252,6 +252,47 @@ files = [
252
  {file = "brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a"},
253
  ]
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  [[package]]
256
  name = "certifi"
257
  version = "2025.11.12"
@@ -271,7 +312,6 @@ description = "Foreign Function Interface for Python calling C code."
271
  optional = false
272
  python-versions = ">=3.9"
273
  groups = ["main"]
274
- markers = "platform_python_implementation != \"PyPy\""
275
  files = [
276
  {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
277
  {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
@@ -591,6 +631,28 @@ ssh = ["bcrypt (>=3.1.5)"]
591
  test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
592
  test-randomorder = ["pytest-randomly"]
593
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
594
  [[package]]
595
  name = "exceptiongroup"
596
  version = "1.3.0"
@@ -610,6 +672,18 @@ typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
610
  [package.extras]
611
  test = ["pytest (>=6)"]
612
 
 
 
 
 
 
 
 
 
 
 
 
 
613
  [[package]]
614
  name = "fastapi"
615
  version = "0.121.2"
@@ -794,6 +868,22 @@ files = [
794
  {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
795
  ]
796
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
797
  [[package]]
798
  name = "hf-xet"
799
  version = "1.2.0"
@@ -830,6 +920,18 @@ files = [
830
  [package.extras]
831
  tests = ["pytest"]
832
 
 
 
 
 
 
 
 
 
 
 
 
 
833
  [[package]]
834
  name = "httpcore"
835
  version = "1.0.9"
@@ -866,9 +968,13 @@ files = [
866
 
867
  [package.dependencies]
868
  anyio = "*"
 
 
869
  certifi = "*"
 
870
  httpcore = "==1.*"
871
  idna = "*"
 
872
 
873
  [package.extras]
874
  brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
@@ -928,6 +1034,18 @@ testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.
928
  torch = ["safetensors[torch]", "torch"]
929
  typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
930
 
 
 
 
 
 
 
 
 
 
 
 
 
931
  [[package]]
932
  name = "idna"
933
  version = "3.11"
@@ -1022,6 +1140,162 @@ files = [
1022
  [package.dependencies]
1023
  referencing = ">=0.31.0"
1024
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1025
  [[package]]
1026
  name = "markdown-it-py"
1027
  version = "4.0.0"
@@ -1633,6 +1907,28 @@ files = [
1633
  dev = ["pre-commit", "tox"]
1634
  testing = ["coverage", "pytest", "pytest-benchmark"]
1635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1636
  [[package]]
1637
  name = "pycparser"
1638
  version = "2.23"
@@ -1640,7 +1936,7 @@ description = "C parser in Python"
1640
  optional = false
1641
  python-versions = ">=3.8"
1642
  groups = ["main"]
1643
- markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""
1644
  files = [
1645
  {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"},
1646
  {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
@@ -2501,18 +2797,18 @@ files = [
2501
 
2502
  [[package]]
2503
  name = "smolagents"
2504
- version = "1.22.0"
2505
  description = "🤗 smolagents: a barebones library for agents. Agents write python code to call tools or orchestrate other agents."
2506
  optional = false
2507
  python-versions = ">=3.10"
2508
  groups = ["main"]
2509
  files = [
2510
- {file = "smolagents-1.22.0-py3-none-any.whl", hash = "sha256:5334adb4e7e5814cd814f1d9ad7efa806ef57f53db40635a29d2bd727774c5f5"},
2511
- {file = "smolagents-1.22.0.tar.gz", hash = "sha256:5fb66f48e3b3ab5e8defcef577a89d5b6dfa8fcb55fc98a58e156cb3c59eb68f"},
2512
  ]
2513
 
2514
  [package.dependencies]
2515
- huggingface-hub = ">=0.31.2"
2516
  jinja2 = ">=3.1.4"
2517
  mcp = {version = "*", optional = true, markers = "extra == \"mcp\""}
2518
  mcpadapt = {version = ">=0.1.13", optional = true, markers = "extra == \"mcp\""}
@@ -2522,9 +2818,10 @@ requests = ">=2.32.3"
2522
  rich = ">=13.9.4"
2523
 
2524
  [package.extras]
2525
- all = ["smolagents[audio,bedrock,docker,e2b,gradio,litellm,mcp,mlx-lm,modal,openai,telemetry,toolkit,transformers,vision]"]
2526
  audio = ["smolagents[torch]", "soundfile"]
2527
  bedrock = ["boto3 (>=1.36.18)"]
 
2528
  dev = ["smolagents[quality,test]", "sqlalchemy"]
2529
  docker = ["docker (>=7.1.0)", "websocket-client"]
2530
  e2b = ["e2b-code-interpreter (>=1.0.3)", "python-dotenv (>=1.0.1)"]
@@ -2541,7 +2838,7 @@ toolkit = ["ddgs (>=9.0.0)", "markdownify (>=0.14.1)"]
2541
  torch = ["numpy (>=1.21.2)", "torch", "torchvision"]
2542
  transformers = ["accelerate", "smolagents[torch]", "transformers (>=4.0.0)"]
2543
  vision = ["helium", "selenium"]
2544
- vllm = ["torch", "vllm"]
2545
 
2546
  [[package]]
2547
  name = "sniffio"
@@ -2555,6 +2852,18 @@ files = [
2555
  {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
2556
  ]
2557
 
 
 
 
 
 
 
 
 
 
 
 
 
2558
  [[package]]
2559
  name = "sse-starlette"
2560
  version = "3.0.3"
@@ -2966,5 +3275,5 @@ files = [
2966
 
2967
  [metadata]
2968
  lock-version = "2.1"
2969
- python-versions = ">=3.10"
2970
- content-hash = "f301d5722b2041165232c770f0524d74195b81151bf2841549067dd23ab77588"
 
252
  {file = "brotli-1.2.0.tar.gz", hash = "sha256:e310f77e41941c13340a95976fe66a8a95b01e783d430eeaf7a2f87e0a57dd0a"},
253
  ]
254
 
255
+ [[package]]
256
+ name = "brotlicffi"
257
+ version = "1.1.0.0"
258
+ description = "Python CFFI bindings to the Brotli library"
259
+ optional = false
260
+ python-versions = ">=3.7"
261
+ groups = ["main"]
262
+ markers = "platform_python_implementation != \"CPython\""
263
+ files = [
264
+ {file = "brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851"},
265
+ {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b"},
266
+ {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9feb210d932ffe7798ee62e6145d3a757eb6233aa9a4e7db78dd3690d7755814"},
267
+ {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84763dbdef5dd5c24b75597a77e1b30c66604725707565188ba54bab4f114820"},
268
+ {file = "brotlicffi-1.1.0.0-cp37-abi3-win32.whl", hash = "sha256:1b12b50e07c3911e1efa3a8971543e7648100713d4e0971b13631cce22c587eb"},
269
+ {file = "brotlicffi-1.1.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:994a4f0681bb6c6c3b0925530a1926b7a189d878e6e5e38fae8efa47c5d9c613"},
270
+ {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e4aeb0bd2540cb91b069dbdd54d458da8c4334ceaf2d25df2f4af576d6766ca"},
271
+ {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b7b0033b0d37bb33009fb2fef73310e432e76f688af76c156b3594389d81391"},
272
+ {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54a07bb2374a1eba8ebb52b6fafffa2afd3c4df85ddd38fcc0511f2bb387c2a8"},
273
+ {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7901a7dc4b88f1c1475de59ae9be59799db1007b7d059817948d8e4f12e24e35"},
274
+ {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce01c7316aebc7fce59da734286148b1d1b9455f89cf2c8a4dfce7d41db55c2d"},
275
+ {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:246f1d1a90279bb6069de3de8d75a8856e073b8ff0b09dcca18ccc14cec85979"},
276
+ {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc4bc5d82bc56ebd8b514fb8350cfac4627d6b0743382e46d033976a5f80fab6"},
277
+ {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c26ecb14386a44b118ce36e546ce307f4810bc9598a6e6cb4f7fca725ae7e6"},
278
+ {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca72968ae4eaf6470498d5c2887073f7efe3b1e7d7ec8be11a06a79cc810e990"},
279
+ {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:add0de5b9ad9e9aa293c3aa4e9deb2b61e99ad6c1634e01d01d98c03e6a354cc"},
280
+ {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b6068e0f3769992d6b622a1cd2e7835eae3cf8d9da123d7f51ca9c1e9c333e5"},
281
+ {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8557a8559509b61e65083f8782329188a250102372576093c88930c875a69838"},
282
+ {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a7ae37e5d79c5bdfb5b4b99f2715a6035e6c5bf538c3746abc8e26694f92f33"},
283
+ {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391151ec86bb1c683835980f4816272a87eaddc46bb91cbf44f62228b84d8cca"},
284
+ {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f3711be9290f0453de8eed5275d93d286abe26b08ab4a35d7452caa1fef532f"},
285
+ {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a807d760763e398bbf2c6394ae9da5815901aa93ee0a37bca5efe78d4ee3171"},
286
+ {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa8ca0623b26c94fccc3a1fdd895be1743b838f3917300506d04aa3346fd2a14"},
287
+ {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3de0cf28a53a3238b252aca9fed1593e9d36c1d116748013339f0949bfc84112"},
288
+ {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6be5ec0e88a4925c91f3dea2bb0013b3a2accda6f77238f76a34a1ea532a1cb0"},
289
+ {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d9eb71bb1085d996244439154387266fd23d6ad37161f6f52f1cd41dd95a3808"},
290
+ {file = "brotlicffi-1.1.0.0.tar.gz", hash = "sha256:b77827a689905143f87915310b93b273ab17888fd43ef350d4832c4a71083c13"},
291
+ ]
292
+
293
+ [package.dependencies]
294
+ cffi = ">=1.0.0"
295
+
296
  [[package]]
297
  name = "certifi"
298
  version = "2025.11.12"
 
312
  optional = false
313
  python-versions = ">=3.9"
314
  groups = ["main"]
 
315
  files = [
316
  {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
317
  {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
 
631
  test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
632
  test-randomorder = ["pytest-randomly"]
633
 
634
+ [[package]]
635
+ name = "ddgs"
636
+ version = "9.9.1"
637
+ description = "Dux Distributed Global Search. A metasearch library that aggregates results from diverse web search services."
638
+ optional = false
639
+ python-versions = ">=3.10"
640
+ groups = ["main"]
641
+ files = [
642
+ {file = "ddgs-9.9.1-py3-none-any.whl", hash = "sha256:bba5701462bc9c8eac69fdd2edf7e833e74741b2bae3593489e470e125e9601d"},
643
+ {file = "ddgs-9.9.1.tar.gz", hash = "sha256:d33529abb56aa5b98eb436565a6bfa48f07eb04dca18d323df723b83cb007e4f"},
644
+ ]
645
+
646
+ [package.dependencies]
647
+ click = ">=8.1.8"
648
+ fake-useragent = ">=2.2.0"
649
+ httpx = {version = ">=0.28.1", extras = ["brotli", "http2", "socks"]}
650
+ lxml = ">=4.9.4"
651
+ primp = ">=0.15.0"
652
+
653
+ [package.extras]
654
+ dev = ["lxml-stubs", "mypy (>=1.17.1)", "pre-commit", "pytest (>=8.4.1)", "pytest-dependency (>=0.6.0)", "ruff (>=0.13.0)", "types-Pygments", "types-pexpect"]
655
+
656
  [[package]]
657
  name = "exceptiongroup"
658
  version = "1.3.0"
 
672
  [package.extras]
673
  test = ["pytest (>=6)"]
674
 
675
+ [[package]]
676
+ name = "fake-useragent"
677
+ version = "2.2.0"
678
+ description = "Up-to-date simple useragent faker with real world database"
679
+ optional = false
680
+ python-versions = ">=3.9"
681
+ groups = ["main"]
682
+ files = [
683
+ {file = "fake_useragent-2.2.0-py3-none-any.whl", hash = "sha256:67f35ca4d847b0d298187443aaf020413746e56acd985a611908c73dba2daa24"},
684
+ {file = "fake_useragent-2.2.0.tar.gz", hash = "sha256:4e6ab6571e40cc086d788523cf9e018f618d07f9050f822ff409a4dfe17c16b2"},
685
+ ]
686
+
687
  [[package]]
688
  name = "fastapi"
689
  version = "0.121.2"
 
868
  {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
869
  ]
870
 
871
+ [[package]]
872
+ name = "h2"
873
+ version = "4.3.0"
874
+ description = "Pure-Python HTTP/2 protocol implementation"
875
+ optional = false
876
+ python-versions = ">=3.9"
877
+ groups = ["main"]
878
+ files = [
879
+ {file = "h2-4.3.0-py3-none-any.whl", hash = "sha256:c438f029a25f7945c69e0ccf0fb951dc3f73a5f6412981daee861431b70e2bdd"},
880
+ {file = "h2-4.3.0.tar.gz", hash = "sha256:6c59efe4323fa18b47a632221a1888bd7fde6249819beda254aeca909f221bf1"},
881
+ ]
882
+
883
+ [package.dependencies]
884
+ hpack = ">=4.1,<5"
885
+ hyperframe = ">=6.1,<7"
886
+
887
  [[package]]
888
  name = "hf-xet"
889
  version = "1.2.0"
 
920
  [package.extras]
921
  tests = ["pytest"]
922
 
923
+ [[package]]
924
+ name = "hpack"
925
+ version = "4.1.0"
926
+ description = "Pure-Python HPACK header encoding"
927
+ optional = false
928
+ python-versions = ">=3.9"
929
+ groups = ["main"]
930
+ files = [
931
+ {file = "hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496"},
932
+ {file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"},
933
+ ]
934
+
935
  [[package]]
936
  name = "httpcore"
937
  version = "1.0.9"
 
968
 
969
  [package.dependencies]
970
  anyio = "*"
971
+ brotli = {version = "*", optional = true, markers = "platform_python_implementation == \"CPython\" and extra == \"brotli\""}
972
+ brotlicffi = {version = "*", optional = true, markers = "platform_python_implementation != \"CPython\" and extra == \"brotli\""}
973
  certifi = "*"
974
+ h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""}
975
  httpcore = "==1.*"
976
  idna = "*"
977
+ socksio = {version = "==1.*", optional = true, markers = "extra == \"socks\""}
978
 
979
  [package.extras]
980
  brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
 
1034
  torch = ["safetensors[torch]", "torch"]
1035
  typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
1036
 
1037
+ [[package]]
1038
+ name = "hyperframe"
1039
+ version = "6.1.0"
1040
+ description = "Pure-Python HTTP/2 framing"
1041
+ optional = false
1042
+ python-versions = ">=3.9"
1043
+ groups = ["main"]
1044
+ files = [
1045
+ {file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"},
1046
+ {file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"},
1047
+ ]
1048
+
1049
  [[package]]
1050
  name = "idna"
1051
  version = "3.11"
 
1140
  [package.dependencies]
1141
  referencing = ">=0.31.0"
1142
 
1143
+ [[package]]
1144
+ name = "lxml"
1145
+ version = "6.0.2"
1146
+ description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
1147
+ optional = false
1148
+ python-versions = ">=3.8"
1149
+ groups = ["main"]
1150
+ files = [
1151
+ {file = "lxml-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e77dd455b9a16bbd2a5036a63ddbd479c19572af81b624e79ef422f929eef388"},
1152
+ {file = "lxml-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d444858b9f07cefff6455b983aea9a67f7462ba1f6cbe4a21e8bf6791bf2153"},
1153
+ {file = "lxml-6.0.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f952dacaa552f3bb8834908dddd500ba7d508e6ea6eb8c52eb2d28f48ca06a31"},
1154
+ {file = "lxml-6.0.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:71695772df6acea9f3c0e59e44ba8ac50c4f125217e84aab21074a1a55e7e5c9"},
1155
+ {file = "lxml-6.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17f68764f35fd78d7c4cc4ef209a184c38b65440378013d24b8aecd327c3e0c8"},
1156
+ {file = "lxml-6.0.2-cp310-cp310-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:058027e261afed589eddcfe530fcc6f3402d7fd7e89bfd0532df82ebc1563dba"},
1157
+ {file = "lxml-6.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8ffaeec5dfea5881d4c9d8913a32d10cfe3923495386106e4a24d45300ef79c"},
1158
+ {file = "lxml-6.0.2-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:f2e3b1a6bb38de0bc713edd4d612969dd250ca8b724be8d460001a387507021c"},
1159
+ {file = "lxml-6.0.2-cp310-cp310-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d6690ec5ec1cce0385cb20896b16be35247ac8c2046e493d03232f1c2414d321"},
1160
+ {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2a50c3c1d11cad0ebebbac357a97b26aa79d2bcaf46f256551152aa85d3a4d1"},
1161
+ {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3efe1b21c7801ffa29a1112fab3b0f643628c30472d507f39544fd48e9549e34"},
1162
+ {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:59c45e125140b2c4b33920d21d83681940ca29f0b83f8629ea1a2196dc8cfe6a"},
1163
+ {file = "lxml-6.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:452b899faa64f1805943ec1c0c9ebeaece01a1af83e130b69cdefeda180bb42c"},
1164
+ {file = "lxml-6.0.2-cp310-cp310-win32.whl", hash = "sha256:1e786a464c191ca43b133906c6903a7e4d56bef376b75d97ccbb8ec5cf1f0a4b"},
1165
+ {file = "lxml-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:dacf3c64ef3f7440e3167aa4b49aa9e0fb99e0aa4f9ff03795640bf94531bcb0"},
1166
+ {file = "lxml-6.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:45f93e6f75123f88d7f0cfd90f2d05f441b808562bf0bc01070a00f53f5028b5"},
1167
+ {file = "lxml-6.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:13e35cbc684aadf05d8711a5d1b5857c92e5e580efa9a0d2be197199c8def607"},
1168
+ {file = "lxml-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b1675e096e17c6fe9c0e8c81434f5736c0739ff9ac6123c87c2d452f48fc938"},
1169
+ {file = "lxml-6.0.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8ac6e5811ae2870953390452e3476694196f98d447573234592d30488147404d"},
1170
+ {file = "lxml-6.0.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5aa0fc67ae19d7a64c3fe725dc9a1bb11f80e01f78289d05c6f62545affec438"},
1171
+ {file = "lxml-6.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de496365750cc472b4e7902a485d3f152ecf57bd3ba03ddd5578ed8ceb4c5964"},
1172
+ {file = "lxml-6.0.2-cp311-cp311-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:200069a593c5e40b8f6fc0d84d86d970ba43138c3e68619ffa234bc9bb806a4d"},
1173
+ {file = "lxml-6.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d2de809c2ee3b888b59f995625385f74629707c9355e0ff856445cdcae682b7"},
1174
+ {file = "lxml-6.0.2-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:b2c3da8d93cf5db60e8858c17684c47d01fee6405e554fb55018dd85fc23b178"},
1175
+ {file = "lxml-6.0.2-cp311-cp311-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:442de7530296ef5e188373a1ea5789a46ce90c4847e597856570439621d9c553"},
1176
+ {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2593c77efde7bfea7f6389f1ab249b15ed4aa5bc5cb5131faa3b843c429fbedb"},
1177
+ {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3e3cb08855967a20f553ff32d147e14329b3ae70ced6edc2f282b94afbc74b2a"},
1178
+ {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ed6c667fcbb8c19c6791bbf40b7268ef8ddf5a96940ba9404b9f9a304832f6c"},
1179
+ {file = "lxml-6.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b8f18914faec94132e5b91e69d76a5c1d7b0c73e2489ea8929c4aaa10b76bbf7"},
1180
+ {file = "lxml-6.0.2-cp311-cp311-win32.whl", hash = "sha256:6605c604e6daa9e0d7f0a2137bdc47a2e93b59c60a65466353e37f8272f47c46"},
1181
+ {file = "lxml-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e5867f2651016a3afd8dd2c8238baa66f1e2802f44bc17e236f547ace6647078"},
1182
+ {file = "lxml-6.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:4197fb2534ee05fd3e7afaab5d8bfd6c2e186f65ea7f9cd6a82809c887bd1285"},
1183
+ {file = "lxml-6.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a59f5448ba2ceccd06995c95ea59a7674a10de0810f2ce90c9006f3cbc044456"},
1184
+ {file = "lxml-6.0.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e8113639f3296706fbac34a30813929e29247718e88173ad849f57ca59754924"},
1185
+ {file = "lxml-6.0.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a8bef9b9825fa8bc816a6e641bb67219489229ebc648be422af695f6e7a4fa7f"},
1186
+ {file = "lxml-6.0.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:65ea18d710fd14e0186c2f973dc60bb52039a275f82d3c44a0e42b43440ea534"},
1187
+ {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c371aa98126a0d4c739ca93ceffa0fd7a5d732e3ac66a46e74339acd4d334564"},
1188
+ {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:700efd30c0fa1a3581d80a748157397559396090a51d306ea59a70020223d16f"},
1189
+ {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c33e66d44fe60e72397b487ee92e01da0d09ba2d66df8eae42d77b6d06e5eba0"},
1190
+ {file = "lxml-6.0.2-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90a345bbeaf9d0587a3aaffb7006aa39ccb6ff0e96a57286c0cb2fd1520ea192"},
1191
+ {file = "lxml-6.0.2-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:064fdadaf7a21af3ed1dcaa106b854077fbeada827c18f72aec9346847cd65d0"},
1192
+ {file = "lxml-6.0.2-cp312-cp312-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fbc74f42c3525ac4ffa4b89cbdd00057b6196bcefe8bce794abd42d33a018092"},
1193
+ {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6ddff43f702905a4e32bc24f3f2e2edfe0f8fde3277d481bffb709a4cced7a1f"},
1194
+ {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6da5185951d72e6f5352166e3da7b0dc27aa70bd1090b0eb3f7f7212b53f1bb8"},
1195
+ {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:57a86e1ebb4020a38d295c04fc79603c7899e0df71588043eb218722dabc087f"},
1196
+ {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:2047d8234fe735ab77802ce5f2297e410ff40f5238aec569ad7c8e163d7b19a6"},
1197
+ {file = "lxml-6.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f91fd2b2ea15a6800c8e24418c0775a1694eefc011392da73bc6cef2623b322"},
1198
+ {file = "lxml-6.0.2-cp312-cp312-win32.whl", hash = "sha256:3ae2ce7d6fedfb3414a2b6c5e20b249c4c607f72cb8d2bb7cc9c6ec7c6f4e849"},
1199
+ {file = "lxml-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:72c87e5ee4e58a8354fb9c7c84cbf95a1c8236c127a5d1b7683f04bed8361e1f"},
1200
+ {file = "lxml-6.0.2-cp312-cp312-win_arm64.whl", hash = "sha256:61cb10eeb95570153e0c0e554f58df92ecf5109f75eacad4a95baa709e26c3d6"},
1201
+ {file = "lxml-6.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9b33d21594afab46f37ae58dfadd06636f154923c4e8a4d754b0127554eb2e77"},
1202
+ {file = "lxml-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8963287d7a4c5c9a432ff487c52e9c5618667179c18a204bdedb27310f022f"},
1203
+ {file = "lxml-6.0.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1941354d92699fb5ffe6ed7b32f9649e43c2feb4b97205f75866f7d21aa91452"},
1204
+ {file = "lxml-6.0.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb2f6ca0ae2d983ded09357b84af659c954722bbf04dea98030064996d156048"},
1205
+ {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb2a12d704f180a902d7fa778c6d71f36ceb7b0d317f34cdc76a5d05aa1dd1df"},
1206
+ {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:6ec0e3f745021bfed19c456647f0298d60a24c9ff86d9d051f52b509663feeb1"},
1207
+ {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:846ae9a12d54e368933b9759052d6206a9e8b250291109c48e350c1f1f49d916"},
1208
+ {file = "lxml-6.0.2-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef9266d2aa545d7374938fb5c484531ef5a2ec7f2d573e62f8ce722c735685fd"},
1209
+ {file = "lxml-6.0.2-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:4077b7c79f31755df33b795dc12119cb557a0106bfdab0d2c2d97bd3cf3dffa6"},
1210
+ {file = "lxml-6.0.2-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a7c5d5e5f1081955358533be077166ee97ed2571d6a66bdba6ec2f609a715d1a"},
1211
+ {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8f8d0cbd0674ee89863a523e6994ac25fd5be9c8486acfc3e5ccea679bad2679"},
1212
+ {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2cbcbf6d6e924c28f04a43f3b6f6e272312a090f269eff68a2982e13e5d57659"},
1213
+ {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dfb874cfa53340009af6bdd7e54ebc0d21012a60a4e65d927c2e477112e63484"},
1214
+ {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fb8dae0b6b8b7f9e96c26fdd8121522ce5de9bb5538010870bd538683d30e9a2"},
1215
+ {file = "lxml-6.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:358d9adae670b63e95bc59747c72f4dc97c9ec58881d4627fe0120da0f90d314"},
1216
+ {file = "lxml-6.0.2-cp313-cp313-win32.whl", hash = "sha256:e8cd2415f372e7e5a789d743d133ae474290a90b9023197fd78f32e2dc6873e2"},
1217
+ {file = "lxml-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:b30d46379644fbfc3ab81f8f82ae4de55179414651f110a1514f0b1f8f6cb2d7"},
1218
+ {file = "lxml-6.0.2-cp313-cp313-win_arm64.whl", hash = "sha256:13dcecc9946dca97b11b7c40d29fba63b55ab4170d3c0cf8c0c164343b9bfdcf"},
1219
+ {file = "lxml-6.0.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:b0c732aa23de8f8aec23f4b580d1e52905ef468afb4abeafd3fec77042abb6fe"},
1220
+ {file = "lxml-6.0.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4468e3b83e10e0317a89a33d28f7aeba1caa4d1a6fd457d115dd4ffe90c5931d"},
1221
+ {file = "lxml-6.0.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:abd44571493973bad4598a3be7e1d807ed45aa2adaf7ab92ab7c62609569b17d"},
1222
+ {file = "lxml-6.0.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:370cd78d5855cfbffd57c422851f7d3864e6ae72d0da615fca4dad8c45d375a5"},
1223
+ {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:901e3b4219fa04ef766885fb40fa516a71662a4c61b80c94d25336b4934b71c0"},
1224
+ {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:a4bf42d2e4cf52c28cc1812d62426b9503cdb0c87a6de81442626aa7d69707ba"},
1225
+ {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2c7fdaa4d7c3d886a42534adec7cfac73860b89b4e5298752f60aa5984641a0"},
1226
+ {file = "lxml-6.0.2-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98a5e1660dc7de2200b00d53fa00bcd3c35a3608c305d45a7bbcaf29fa16e83d"},
1227
+ {file = "lxml-6.0.2-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:dc051506c30b609238d79eda75ee9cab3e520570ec8219844a72a46020901e37"},
1228
+ {file = "lxml-6.0.2-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8799481bbdd212470d17513a54d568f44416db01250f49449647b5ab5b5dccb9"},
1229
+ {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9261bb77c2dab42f3ecd9103951aeca2c40277701eb7e912c545c1b16e0e4917"},
1230
+ {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:65ac4a01aba353cfa6d5725b95d7aed6356ddc0a3cd734de00124d285b04b64f"},
1231
+ {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b22a07cbb82fea98f8a2fd814f3d1811ff9ed76d0fc6abc84eb21527596e7cc8"},
1232
+ {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d759cdd7f3e055d6bc8d9bec3ad905227b2e4c785dc16c372eb5b5e83123f48a"},
1233
+ {file = "lxml-6.0.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:945da35a48d193d27c188037a05fec5492937f66fb1958c24fc761fb9d40d43c"},
1234
+ {file = "lxml-6.0.2-cp314-cp314-win32.whl", hash = "sha256:be3aaa60da67e6153eb15715cc2e19091af5dc75faef8b8a585aea372507384b"},
1235
+ {file = "lxml-6.0.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa25afbadead523f7001caf0c2382afd272c315a033a7b06336da2637d92d6ed"},
1236
+ {file = "lxml-6.0.2-cp314-cp314-win_arm64.whl", hash = "sha256:063eccf89df5b24e361b123e257e437f9e9878f425ee9aae3144c77faf6da6d8"},
1237
+ {file = "lxml-6.0.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:6162a86d86893d63084faaf4ff937b3daea233e3682fb4474db07395794fa80d"},
1238
+ {file = "lxml-6.0.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:414aaa94e974e23a3e92e7ca5b97d10c0cf37b6481f50911032c69eeb3991bba"},
1239
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48461bd21625458dd01e14e2c38dd0aea69addc3c4f960c30d9f59d7f93be601"},
1240
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:25fcc59afc57d527cfc78a58f40ab4c9b8fd096a9a3f964d2781ffb6eb33f4ed"},
1241
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5179c60288204e6ddde3f774a93350177e08876eaf3ab78aa3a3649d43eb7d37"},
1242
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:967aab75434de148ec80597b75062d8123cadf2943fb4281f385141e18b21338"},
1243
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d100fcc8930d697c6561156c6810ab4a508fb264c8b6779e6e61e2ed5e7558f9"},
1244
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ca59e7e13e5981175b8b3e4ab84d7da57993eeff53c07764dcebda0d0e64ecd"},
1245
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:957448ac63a42e2e49531b9d6c0fa449a1970dbc32467aaad46f11545be9af1d"},
1246
+ {file = "lxml-6.0.2-cp314-cp314t-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b7fc49c37f1786284b12af63152fe1d0990722497e2d5817acfe7a877522f9a9"},
1247
+ {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e19e0643cc936a22e837f79d01a550678da8377d7d801a14487c10c34ee49c7e"},
1248
+ {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:1db01e5cf14345628e0cbe71067204db658e2fb8e51e7f33631f5f4735fefd8d"},
1249
+ {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:875c6b5ab39ad5291588aed6925fac99d0097af0dd62f33c7b43736043d4a2ec"},
1250
+ {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cdcbed9ad19da81c480dfd6dd161886db6096083c9938ead313d94b30aadf272"},
1251
+ {file = "lxml-6.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:80dadc234ebc532e09be1975ff538d154a7fa61ea5031c03d25178855544728f"},
1252
+ {file = "lxml-6.0.2-cp314-cp314t-win32.whl", hash = "sha256:da08e7bb297b04e893d91087df19638dc7a6bb858a954b0cc2b9f5053c922312"},
1253
+ {file = "lxml-6.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:252a22982dca42f6155125ac76d3432e548a7625d56f5a273ee78a5057216eca"},
1254
+ {file = "lxml-6.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:bb4c1847b303835d89d785a18801a883436cdfd5dc3d62947f9c49e24f0f5a2c"},
1255
+ {file = "lxml-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a656ca105115f6b766bba324f23a67914d9c728dafec57638e2b92a9dcd76c62"},
1256
+ {file = "lxml-6.0.2-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c54d83a2188a10ebdba573f16bd97135d06c9ef60c3dc495315c7a28c80a263f"},
1257
+ {file = "lxml-6.0.2-cp38-cp38-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:1ea99340b3c729beea786f78c38f60f4795622f36e305d9c9be402201efdc3b7"},
1258
+ {file = "lxml-6.0.2-cp38-cp38-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:af85529ae8d2a453feee4c780d9406a5e3b17cee0dd75c18bd31adcd584debc3"},
1259
+ {file = "lxml-6.0.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fe659f6b5d10fb5a17f00a50eb903eb277a71ee35df4615db573c069bcf967ac"},
1260
+ {file = "lxml-6.0.2-cp38-cp38-win32.whl", hash = "sha256:5921d924aa5468c939d95c9814fa9f9b5935a6ff4e679e26aaf2951f74043512"},
1261
+ {file = "lxml-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:0aa7070978f893954008ab73bb9e3c24a7c56c054e00566a21b553dc18105fca"},
1262
+ {file = "lxml-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2c8458c2cdd29589a8367c09c8f030f1d202be673f0ca224ec18590b3b9fb694"},
1263
+ {file = "lxml-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3fee0851639d06276e6b387f1c190eb9d7f06f7f53514e966b26bae46481ec90"},
1264
+ {file = "lxml-6.0.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b2142a376b40b6736dfc214fd2902409e9e3857eff554fed2d3c60f097e62a62"},
1265
+ {file = "lxml-6.0.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6b5b39cc7e2998f968f05309e666103b53e2edd01df8dc51b90d734c0825444"},
1266
+ {file = "lxml-6.0.2-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4aec24d6b72ee457ec665344a29acb2d35937d5192faebe429ea02633151aad"},
1267
+ {file = "lxml-6.0.2-cp39-cp39-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:b42f4d86b451c2f9d06ffb4f8bbc776e04df3ba070b9fe2657804b1b40277c48"},
1268
+ {file = "lxml-6.0.2-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cdaefac66e8b8f30e37a9b4768a391e1f8a16a7526d5bc77a7928408ef68e93"},
1269
+ {file = "lxml-6.0.2-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:b738f7e648735714bbb82bdfd030203360cfeab7f6e8a34772b3c8c8b820568c"},
1270
+ {file = "lxml-6.0.2-cp39-cp39-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:daf42de090d59db025af61ce6bdb2521f0f102ea0e6ea310f13c17610a97da4c"},
1271
+ {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:66328dabea70b5ba7e53d94aa774b733cf66686535f3bc9250a7aab53a91caaf"},
1272
+ {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:e237b807d68a61fc3b1e845407e27e5eb8ef69bc93fe8505337c1acb4ee300b6"},
1273
+ {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ac02dc29fd397608f8eb15ac1610ae2f2f0154b03f631e6d724d9e2ad4ee2c84"},
1274
+ {file = "lxml-6.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:817ef43a0c0b4a77bd166dc9a09a555394105ff3374777ad41f453526e37f9cb"},
1275
+ {file = "lxml-6.0.2-cp39-cp39-win32.whl", hash = "sha256:bc532422ff26b304cfb62b328826bd995c96154ffd2bac4544f37dbb95ecaa8f"},
1276
+ {file = "lxml-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:995e783eb0374c120f528f807443ad5a83a656a8624c467ea73781fc5f8a8304"},
1277
+ {file = "lxml-6.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:08b9d5e803c2e4725ae9e8559ee880e5328ed61aa0935244e0515d7d9dbec0aa"},
1278
+ {file = "lxml-6.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e748d4cf8fef2526bb2a589a417eba0c8674e29ffcb570ce2ceca44f1e567bf6"},
1279
+ {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ddb1049fa0579d0cbd00503ad8c58b9ab34d1254c77bc6a5576d96ec7853dba"},
1280
+ {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cb233f9c95f83707dae461b12b720c1af9c28c2d19208e1be03387222151daf5"},
1281
+ {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc456d04db0515ce3320d714a1eac7a97774ff0849e7718b492d957da4631dd4"},
1282
+ {file = "lxml-6.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2613e67de13d619fd283d58bda40bff0ee07739f624ffee8b13b631abf33083d"},
1283
+ {file = "lxml-6.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:24a8e756c982c001ca8d59e87c80c4d9dcd4d9b44a4cbeb8d9be4482c514d41d"},
1284
+ {file = "lxml-6.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1c06035eafa8404b5cf475bb37a9f6088b0aca288d4ccc9d69389750d5543700"},
1285
+ {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c7d13103045de1bdd6fe5d61802565f1a3537d70cd3abf596aa0af62761921ee"},
1286
+ {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a3c150a95fbe5ac91de323aa756219ef9cf7fde5a3f00e2281e30f33fa5fa4f"},
1287
+ {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60fa43be34f78bebb27812ed90f1925ec99560b0fa1decdb7d12b84d857d31e9"},
1288
+ {file = "lxml-6.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:21c73b476d3cfe836be731225ec3421fa2f048d84f6df6a8e70433dff1376d5a"},
1289
+ {file = "lxml-6.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:27220da5be049e936c3aca06f174e8827ca6445a4353a1995584311487fc4e3e"},
1290
+ {file = "lxml-6.0.2.tar.gz", hash = "sha256:cd79f3367bd74b317dda655dc8fcfa304d9eb6e4fb06b7168c5cf27f96e0cd62"},
1291
+ ]
1292
+
1293
+ [package.extras]
1294
+ cssselect = ["cssselect (>=0.7)"]
1295
+ html-clean = ["lxml_html_clean"]
1296
+ html5 = ["html5lib"]
1297
+ htmlsoup = ["BeautifulSoup4"]
1298
+
1299
  [[package]]
1300
  name = "markdown-it-py"
1301
  version = "4.0.0"
 
1907
  dev = ["pre-commit", "tox"]
1908
  testing = ["coverage", "pytest", "pytest-benchmark"]
1909
 
1910
+ [[package]]
1911
+ name = "primp"
1912
+ version = "0.15.0"
1913
+ description = "HTTP client that can impersonate web browsers, mimicking their headers and `TLS/JA3/JA4/HTTP2` fingerprints"
1914
+ optional = false
1915
+ python-versions = ">=3.8"
1916
+ groups = ["main"]
1917
+ files = [
1918
+ {file = "primp-0.15.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:1b281f4ca41a0c6612d4c6e68b96e28acfe786d226a427cd944baa8d7acd644f"},
1919
+ {file = "primp-0.15.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:489cbab55cd793ceb8f90bb7423c6ea64ebb53208ffcf7a044138e3c66d77299"},
1920
+ {file = "primp-0.15.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b45c23f94016215f62d2334552224236217aaeb716871ce0e4dcfa08eb161"},
1921
+ {file = "primp-0.15.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e985a9cba2e3f96a323722e5440aa9eccaac3178e74b884778e926b5249df080"},
1922
+ {file = "primp-0.15.0-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:6b84a6ffa083e34668ff0037221d399c24d939b5629cd38223af860de9e17a83"},
1923
+ {file = "primp-0.15.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:592f6079646bdf5abbbfc3b0a28dac8de943f8907a250ce09398cda5eaebd260"},
1924
+ {file = "primp-0.15.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a728e5a05f37db6189eb413d22c78bd143fa59dd6a8a26dacd43332b3971fe8"},
1925
+ {file = "primp-0.15.0-cp38-abi3-win_amd64.whl", hash = "sha256:aeb6bd20b06dfc92cfe4436939c18de88a58c640752cf7f30d9e4ae893cdec32"},
1926
+ {file = "primp-0.15.0.tar.gz", hash = "sha256:1af8ea4b15f57571ff7fc5e282a82c5eb69bc695e19b8ddeeda324397965b30a"},
1927
+ ]
1928
+
1929
+ [package.extras]
1930
+ dev = ["certifi", "mypy (>=1.14.1)", "pytest (>=8.1.1)", "pytest-asyncio (>=0.25.3)", "ruff (>=0.9.2)", "typing-extensions ; python_full_version < \"3.12.0\""]
1931
+
1932
  [[package]]
1933
  name = "pycparser"
1934
  version = "2.23"
 
1936
  optional = false
1937
  python-versions = ">=3.8"
1938
  groups = ["main"]
1939
+ markers = "implementation_name != \"PyPy\""
1940
  files = [
1941
  {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"},
1942
  {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
 
2797
 
2798
  [[package]]
2799
  name = "smolagents"
2800
+ version = "1.23.0"
2801
  description = "🤗 smolagents: a barebones library for agents. Agents write python code to call tools or orchestrate other agents."
2802
  optional = false
2803
  python-versions = ">=3.10"
2804
  groups = ["main"]
2805
  files = [
2806
+ {file = "smolagents-1.23.0-py3-none-any.whl", hash = "sha256:4851cb6a27e5adc1b916b61991d1d2a7cedf4947677d0ae92b3a9cc6d29c5528"},
2807
+ {file = "smolagents-1.23.0.tar.gz", hash = "sha256:a8ead28a85a02a9f1bcf25016db6bdc3ba2cb456912e336fa7fabd9acf4203bc"},
2808
  ]
2809
 
2810
  [package.dependencies]
2811
+ huggingface-hub = ">=0.31.2,<1.0.0"
2812
  jinja2 = ">=3.1.4"
2813
  mcp = {version = "*", optional = true, markers = "extra == \"mcp\""}
2814
  mcpadapt = {version = ">=0.1.13", optional = true, markers = "extra == \"mcp\""}
 
2818
  rich = ">=13.9.4"
2819
 
2820
  [package.extras]
2821
+ all = ["smolagents[audio,bedrock,blaxel,docker,e2b,gradio,litellm,mcp,mlx-lm,modal,openai,telemetry,toolkit,transformers,vision]"]
2822
  audio = ["smolagents[torch]", "soundfile"]
2823
  bedrock = ["boto3 (>=1.36.18)"]
2824
+ blaxel = ["blaxel (>=0.2.19)", "websocket-client"]
2825
  dev = ["smolagents[quality,test]", "sqlalchemy"]
2826
  docker = ["docker (>=7.1.0)", "websocket-client"]
2827
  e2b = ["e2b-code-interpreter (>=1.0.3)", "python-dotenv (>=1.0.1)"]
 
2838
  torch = ["numpy (>=1.21.2)", "torch", "torchvision"]
2839
  transformers = ["accelerate", "smolagents[torch]", "transformers (>=4.0.0)"]
2840
  vision = ["helium", "selenium"]
2841
+ vllm = ["torch", "vllm (>=0.10.2)"]
2842
 
2843
  [[package]]
2844
  name = "sniffio"
 
2852
  {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
2853
  ]
2854
 
2855
+ [[package]]
2856
+ name = "socksio"
2857
+ version = "1.0.0"
2858
+ description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5."
2859
+ optional = false
2860
+ python-versions = ">=3.6"
2861
+ groups = ["main"]
2862
+ files = [
2863
+ {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"},
2864
+ {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"},
2865
+ ]
2866
+
2867
  [[package]]
2868
  name = "sse-starlette"
2869
  version = "3.0.3"
 
3275
 
3276
  [metadata]
3277
  lock-version = "2.1"
3278
+ python-versions = ">=3.10,<3.15"
3279
+ content-hash = "cb3d331a7047ab0fb7e4a97e59ef8a75933ee86e4ac6e126fa8ec67299925aa5"
pyproject.toml CHANGED
@@ -18,6 +18,7 @@ dependencies = [
18
  "transformers (>=4.57.1,<5.0.0)",
19
  "typer[all] (>=0.20.0,<0.21.0)",
20
  "gradio (>=5.49.1,<6.0.0)",
 
21
  ]
22
 
23
 
 
18
  "transformers (>=4.57.1,<5.0.0)",
19
  "typer[all] (>=0.20.0,<0.21.0)",
20
  "gradio (>=5.49.1,<6.0.0)",
21
+ "ddgs (>=9.9.1,<10.0.0)",
22
  ]
23
 
24
 
src/upgrade_advisor/agents/package.py CHANGED
@@ -1,14 +1,10 @@
1
- import ast
2
- import json
3
  import logging
4
- from typing import Any, Iterator, Optional
5
 
6
  from pydantic import BaseModel
7
- from smolagents import CodeAgent, ToolCall, ToolOutput
8
  from smolagents.agents import StreamEvent
9
  from smolagents.mcp_client import MCPClient
10
- from smolagents.memory import ActionStep, FinalAnswerStep, PlanningStep
11
- from smolagents.models import ChatMessageStreamDelta, ChatMessageToolCall
12
 
13
  from ..schema import ( # noqa
14
  GithubRepoSchema,
@@ -42,105 +38,6 @@ def map_tool_call_to_schema(tool_name: str) -> Optional[type[BaseModel]]:
42
  # See https://github.com/huggingface/smolagents/pull/1660
43
 
44
 
45
- def to_event_schema(evt: Any) -> Optional[StreamEvent]:
46
- """Convert various event types to standardized StreamEvent schemas."""
47
- if isinstance(
48
- evt,
49
- (
50
- ChatMessageStreamDelta,
51
- PlanningStep,
52
- ActionStep,
53
- ToolCall,
54
- ),
55
- ):
56
- return evt
57
-
58
- # Tool output (result from a tool execution)
59
- if isinstance(evt, ToolOutput):
60
- tool_call = getattr(evt, "tool_call", None)
61
- tool_name = getattr(tool_call, "name", None) if tool_call is not None else None
62
- schema = map_tool_call_to_schema(tool_name) if tool_name else None
63
-
64
- # Normalize output to a Python object first
65
- raw_output = getattr(evt, "output", None)
66
- normalized_output = raw_output
67
- """
68
- IMPORTANT:
69
- Thing to note is that the MCP can
70
- return outputs in different formats:
71
- - JSON strings (e.g., '{"key": "value"}')
72
- - Python-literal strings (e.g., "{'key': 'value'}")
73
- - Direct Python objects (e.g., dicts, lists)
74
- We need to handle these cases to convert them into a consistent Python object.
75
- """
76
- if isinstance(raw_output, str):
77
- # Try JSON first
78
- try:
79
- normalized_output = json.loads(raw_output)
80
- except Exception:
81
- # Fallback to Python-literal parsing (single-quoted dicts)
82
- try:
83
- normalized_output = ast.literal_eval(raw_output)
84
- except Exception:
85
- logger.warning(
86
- f"Failed to parse tool output string for tool '{tool_name}'. Returning raw output."
87
- )
88
- normalized_output = raw_output # keep as-is if not parseable
89
-
90
- # if dict or list, keep as is
91
- parsed_output = normalized_output
92
- if schema is not None:
93
- try:
94
- # Pydantic v2: model_validate; fallback to parse_obj for v1
95
- if hasattr(schema, "model_validate"):
96
- parsed_output = schema.model_validate(normalized_output)
97
- else:
98
- parsed_output = schema.parse_obj(normalized_output)
99
- logger.info(
100
- f"Successfully parsed tool output for tool '{tool_name}' into schema '{schema.__name__}'."
101
- )
102
- except Exception as e:
103
- logger.warning(
104
- f"Failed to parse tool output for tool '{tool_name}' into schema '{schema.__name__}': {e}. Returning normalized output."
105
- )
106
-
107
- # Return a new ToolOutput with normalized output and original tool_call
108
- return ToolOutput(
109
- id=getattr(evt, "id", ""),
110
- output=parsed_output,
111
- is_final_answer=getattr(evt, "is_final_answer", False),
112
- observation=getattr(evt, "observation", None),
113
- tool_call=tool_call,
114
- )
115
-
116
- # Final answer
117
- if isinstance(evt, FinalAnswerStep):
118
- output = getattr(evt, "output", None)
119
- return FinalAnswerStep(output=output)
120
-
121
- # ChatMessageToolCall (non-streaming tool call object sometimes yielded by models)
122
- if isinstance(evt, ChatMessageToolCall):
123
- func = getattr(evt, "function", None)
124
- name = getattr(func, "name", None)
125
- arguments = getattr(func, "arguments", None)
126
- if isinstance(arguments, str):
127
- try:
128
- arguments = json.loads(arguments)
129
- except Exception:
130
- logger.warning(
131
- f"Failed to parse arguments string for tool '{name}'. Returning raw arguments."
132
- )
133
- pass
134
- return ToolCall(
135
- id=getattr(evt, "id", None),
136
- name=name,
137
- arguments=arguments,
138
- )
139
-
140
- # Ignore unknown event types or return None
141
- return None
142
-
143
-
144
  class PackageDiscoveryAgent:
145
  """Agent that discovers metadata about Python packages using MCP tools."""
146
 
@@ -154,6 +51,7 @@ class PackageDiscoveryAgent:
154
  tools=tools,
155
  model=model,
156
  max_steps=10,
 
157
  additional_authorized_imports=[
158
  "json",
159
  "datetime",
@@ -166,36 +64,39 @@ class PackageDiscoveryAgent:
166
  )
167
  logger.info(f"PackageDiscoveryAgent initialized with model and tools: {tools}.")
168
 
169
- def _discover_package_info(self, user_input: str) -> Iterator[StreamEvent]:
 
 
170
  """Discover package information based on user input.
171
 
172
  Yields StreamEvent items as the agent runs (planning, action, tool calls,
173
  tool outputs, final answer, and streaming deltas). Each yielded event
174
  can be normalized further by the caller if needed.
175
  """
176
- prompt = get_package_discovery_prompt(user_input)
177
  logger.info(f"Running agent with max_steps: {self.agent.max_steps}.")
178
  try:
179
- for event in self.agent.run(
180
- prompt,
181
- max_steps=self.agent.max_steps,
182
- stream=True,
183
- ):
184
- normalized = to_event_schema(event)
185
- if normalized is not None:
186
- yield normalized
187
 
188
  except Exception as e:
189
  logger.error(f"Error discovering package info: {e}")
190
- yield {
191
  "name": "unknown",
192
  "version": "unknown",
193
  "summary": "Error occurred: " + str(e),
194
  }
195
 
196
- def discover_package_info(self, user_input: str):
 
 
197
  """Public method to start package discovery."""
198
- return self._discover_package_info(user_input)
 
 
199
 
200
 
201
  if __name__ == "__main__": # Example usage of PackageDiscoveryAgent
 
 
 
1
  import logging
2
+ from typing import Iterator, Optional
3
 
4
  from pydantic import BaseModel
5
+ from smolagents import CodeAgent
6
  from smolagents.agents import StreamEvent
7
  from smolagents.mcp_client import MCPClient
 
 
8
 
9
  from ..schema import ( # noqa
10
  GithubRepoSchema,
 
38
  # See https://github.com/huggingface/smolagents/pull/1660
39
 
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  class PackageDiscoveryAgent:
42
  """Agent that discovers metadata about Python packages using MCP tools."""
43
 
 
51
  tools=tools,
52
  model=model,
53
  max_steps=10,
54
+ add_base_tools=True,
55
  additional_authorized_imports=[
56
  "json",
57
  "datetime",
 
64
  )
65
  logger.info(f"PackageDiscoveryAgent initialized with model and tools: {tools}.")
66
 
67
+ def _discover_package_info(
68
+ self, user_input: str, reframed_question: str = None
69
+ ) -> Iterator[StreamEvent]:
70
  """Discover package information based on user input.
71
 
72
  Yields StreamEvent items as the agent runs (planning, action, tool calls,
73
  tool outputs, final answer, and streaming deltas). Each yielded event
74
  can be normalized further by the caller if needed.
75
  """
76
+ prompt = get_package_discovery_prompt(user_input, reframed_question)
77
  logger.info(f"Running agent with max_steps: {self.agent.max_steps}.")
78
  try:
79
+ result = self.agent.run(task=prompt, max_steps=self.agent.max_steps)
80
+ logger.info(
81
+ f"Package discovery completed successfully. The return type of result: {type(result)}"
82
+ )
83
+ return result
 
 
 
84
 
85
  except Exception as e:
86
  logger.error(f"Error discovering package info: {e}")
87
+ return {
88
  "name": "unknown",
89
  "version": "unknown",
90
  "summary": "Error occurred: " + str(e),
91
  }
92
 
93
+ def discover_package_info(
94
+ self, user_input: str, reframed_question: str = None
95
+ ) -> Iterator[StreamEvent]:
96
  """Public method to start package discovery."""
97
+ return self._discover_package_info(
98
+ user_input, reframed_question=reframed_question
99
+ )
100
 
101
 
102
  if __name__ == "__main__": # Example usage of PackageDiscoveryAgent
src/upgrade_advisor/agents/prompts.py CHANGED
@@ -8,7 +8,17 @@ from ..schema import (
8
  )
9
 
10
 
11
- def get_package_discovery_prompt(user_input: str) -> str:
 
 
 
 
 
 
 
 
 
 
12
  return f"""
13
  You are a package discovery agent that discovers metadata about
14
  Python PyPI packages.
@@ -61,7 +71,6 @@ def get_package_discovery_prompt(user_input: str) -> str:
61
  if isinstance(d, dict) and "info" in d and isinstance(d["info"], dict):
62
  latest = d["info"].get("version") or _extract_version_fallback(str(d))
63
 
64
- QUESTION:
65
  {user_input}
66
 
67
  SCHEMA DETAILS (use these exact shapes):
@@ -85,20 +94,12 @@ def get_package_discovery_prompt(user_input: str) -> str:
85
  - MCP tool outputs are often structured (Python dict/list). Use them directly.
86
  - If you get a string result, call _to_mapping(result) BEFORE indexing like result["info"].
87
  - Also be careful of the types. Some fields may be optional or missing. Some fields are ints/floats.
88
- - For version numbers, use the `packaging.version` module.\
89
- ```python
90
- from packaging.version import parse
91
- # 1. Numeric versions that break string comparison
92
- print(parse("1.10") > parse("1.2"))
93
- # 2. Release vs pre-release
94
- print(parse("1.0") > parse("1.0rc1"))
95
- # 3. Stable vs beta
96
- print(parse("2.0") > parse("2.0b1"))
97
- # 4. Different-length segments
98
- print(parse("1.2") == parse("1.2.0"))
99
- # 5. Dev releases
100
- print(parse("3.0.dev2") < parse("3.0"))
101
- ```
102
  - Never use ast/json modules outside the helpers; import them once at the top and only call _to_mapping / _extract_version_fallback.
103
  - When you have gathered the required info, call final_answer with the BEST structured object
104
  that answers the user query according to the appropriate schema.
 
8
  )
9
 
10
 
11
+ def get_package_discovery_prompt(
12
+ original_question: str, reframed_question: str = None
13
+ ) -> str:
14
+ user_input = f"""
15
+ USER QUESTION:
16
+ {original_question}
17
+ """
18
+ if reframed_question:
19
+ user_input += f"\nREFRAMED QUESTION (LLM-generated):\n{reframed_question}\n"
20
+
21
+ # Add the rest of the prompt content here...
22
  return f"""
23
  You are a package discovery agent that discovers metadata about
24
  Python PyPI packages.
 
71
  if isinstance(d, dict) and "info" in d and isinstance(d["info"], dict):
72
  latest = d["info"].get("version") or _extract_version_fallback(str(d))
73
 
 
74
  {user_input}
75
 
76
  SCHEMA DETAILS (use these exact shapes):
 
94
  - MCP tool outputs are often structured (Python dict/list). Use them directly.
95
  - If you get a string result, call _to_mapping(result) BEFORE indexing like result["info"].
96
  - Also be careful of the types. Some fields may be optional or missing. Some fields are ints/floats.
97
+ - Always prefer MCP tool data over web search data for package metadata.
98
+ - However, If you use the `web_search`, you must only rely on documentation
99
+ from the official package website, PyPI page, or official GitHub repo.
100
+ - If the `web_search` tool is used, ALWAYS validate the info with MCP tool data if possible.
101
+ - NEVER fabricate data. If you cannot find the info, say so.
102
+ - For parsing version numbers, use the `packaging.version` module.
 
 
 
 
 
 
 
 
103
  - Never use ast/json modules outside the helpers; import them once at the top and only call _to_mapping / _extract_version_fallback.
104
  - When you have gathered the required info, call final_answer with the BEST structured object
105
  that answers the user query according to the appropriate schema.
src/upgrade_advisor/chat/chat.py CHANGED
@@ -1,13 +1,23 @@
 
1
  import os
2
 
3
  import requests
4
  from dotenv import load_dotenv
5
 
6
- from .prompts import query_rewriter_prompt, result_package_summary_prompt
 
 
 
 
7
 
8
  load_dotenv()
9
 
10
 
 
 
 
 
 
11
  async def query(payload):
12
  API_URL = "https://router.huggingface.co/v1/chat/completions"
13
  headers = {
@@ -27,7 +37,7 @@ def extract_answer_content(answer_content: str) -> str:
27
 
28
 
29
  async def run_document_qa(
30
- question: str, context: str, original_question: str = ""
31
  ) -> str:
32
  response = await query(
33
  {
@@ -36,8 +46,8 @@ async def run_document_qa(
36
  "role": "user",
37
  "content": result_package_summary_prompt(
38
  context,
39
- question,
40
- original_question,
41
  ),
42
  }
43
  ],
@@ -50,13 +60,87 @@ async def run_document_qa(
50
  return extract_answer_content(answer["content"])
51
 
52
 
53
- async def qn_rewriter(original_question: str) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  response = await query(
55
  {
56
  "messages": [
57
  {
58
  "role": "user",
59
- "content": query_rewriter_prompt(original_question),
 
 
60
  }
61
  ],
62
  # "model": "Qwen/Qwen3-4B-Thinking-2507",
@@ -65,4 +149,47 @@ async def qn_rewriter(original_question: str) -> str:
65
  )
66
 
67
  answer = response["choices"][0]["message"]
68
- return extract_answer_content(answer["content"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
  import os
3
 
4
  import requests
5
  from dotenv import load_dotenv
6
 
7
+ from .prompts import (
8
+ chat_summarizer_prompt,
9
+ query_rewriter_prompt,
10
+ result_package_summary_prompt,
11
+ )
12
 
13
  load_dotenv()
14
 
15
 
16
+ logger = logging.getLogger(__name__)
17
+ logger.setLevel(logging.INFO)
18
+ logger.addHandler(logging.StreamHandler())
19
+
20
+
21
  async def query(payload):
22
  API_URL = "https://router.huggingface.co/v1/chat/completions"
23
  headers = {
 
37
 
38
 
39
  async def run_document_qa(
40
+ question: str, context: str, rewritten_question: str = None
41
  ) -> str:
42
  response = await query(
43
  {
 
46
  "role": "user",
47
  "content": result_package_summary_prompt(
48
  context,
49
+ original_question=question,
50
+ rewritten_question=rewritten_question,
51
  ),
52
  }
53
  ],
 
60
  return extract_answer_content(answer["content"])
61
 
62
 
63
+ async def qn_rewriter_judge(original_question: str, rewritten_question: str) -> str:
64
+ response = await query(
65
+ {
66
+ "messages": [
67
+ {
68
+ "role": "user",
69
+ "content": f"""
70
+ You are a judge that evaluates whether a rewritten question
71
+ captures the intent of the original question.
72
+ Note that the rewritten question may include details from
73
+ the chat history, but you should focus on whether the core
74
+ intent of the original question is preserved. The
75
+ additional history context will not change the user's intent, but
76
+ may add clarifications.
77
+ Return the word "YES" if it does, otherwise "NO". No additional
78
+ explanation. Never return anything other than "YES" or "NO".
79
+
80
+ EXAMPLE 1:
81
+ `ORIGINAL QUESTION: latest version of nnumpy?`
82
+ `REWRITTEN QUESTION: What is the latest version of numpy?`
83
+ Answer: YES
84
+
85
+ EXAMPLE 2:
86
+ `ORIGINAL QUESTION: Show me the dev docu link of requests
87
+ liberary.`
88
+ `REWRITTEN QUESTION: Show me the user guide of the requests
89
+ library.`
90
+ Answer: NO
91
+
92
+ EXAMPLE 3:
93
+ `ORIGINAL QUESTION: How to install fapi?`
94
+ `REWRITTEN QUESTION: What is the dependency list of fastapi?`
95
+ Answer: NO
96
+
97
+ EXAMPLE 4:
98
+ `ORIGINAL QUESTION: The user had trouble with a version of
99
+ pandas. User tried downgrading to 1.2.0 but it didn't help
100
+ and has compatibility issues with other packages like numpy
101
+ of version 1.19. What version should I use?`
102
+ `REWRITTEN QUESTION: Which version of pandas is most stable
103
+ with version 1.19 of numpy?`
104
+ Answer: YES
105
+
106
+ EXAMPLE 5:
107
+ `ORIGINAL QUESTION: The user is talking about issues with
108
+ version 2.0.0 of requests library. They mentioned that it
109
+ broke their existing code that worked with version numpy 1.2.3. I
110
+ am looking for a version that is compatible.`
111
+ `REWRITTEN QUESTION: Which version of requests is compatible
112
+ with version numpy 1.2.3?`
113
+ Answer: YES
114
+
115
+
116
+ ORIGINAL QUESTION: {original_question}\n
117
+ REWRITTEN QUESTION: {rewritten_question}\n
118
+ Answer:
119
+ """,
120
+ }
121
+ ],
122
+ # "model": "Qwen/Qwen3-4B-Thinking-2507",
123
+ "model": "Qwen/Qwen2.5-Coder-32B-Instruct",
124
+ }
125
+ )
126
+ answer = response["choices"][0]["message"]
127
+ answer_text = extract_answer_content(answer["content"])
128
+ logger.info(f"Question Rewriter judge answer: {answer_text}")
129
+ if answer_text.strip().upper() == "YES":
130
+ return True
131
+ else:
132
+ return False
133
+
134
+
135
+ async def qn_rewriter(original_question: str, summarized_history: str = "") -> str:
136
  response = await query(
137
  {
138
  "messages": [
139
  {
140
  "role": "user",
141
+ "content": query_rewriter_prompt(
142
+ original_question, summarized_history
143
+ ),
144
  }
145
  ],
146
  # "model": "Qwen/Qwen3-4B-Thinking-2507",
 
149
  )
150
 
151
  answer = response["choices"][0]["message"]
152
+ rewritten_question = extract_answer_content(answer["content"])
153
+ is_good = await qn_rewriter_judge(original_question, rewritten_question)
154
+
155
+ return rewritten_question, is_good
156
+
157
+
158
+ async def summarize_chat_history(
159
+ history: list[dict],
160
+ turns_cutoff=10,
161
+ word_cutoff=100,
162
+ ) -> str:
163
+ # history is a list of dicts with 'role' and 'content' keys
164
+ # [{"role": "user", "content": "..."}, {"role": "assistant", "content":
165
+ # "..."}]
166
+ logger.info(f"Summarizing chat history with {len(history)} turns.")
167
+ logger.info(f"Using last {turns_cutoff} turns with {word_cutoff} words each.")
168
+ chat_history_text = ""
169
+ for turn in history[-turns_cutoff:]:
170
+ # take only the last `cutoff` turns
171
+ role = turn["role"]
172
+ content = turn["content"]
173
+ if len(content.split()) > word_cutoff:
174
+ content = " ".join(content.split()[:word_cutoff]) + " [TRUNCATED]"
175
+ chat_history_text += f"{role.upper()}:\n{content}\n\n"
176
+
177
+ logger.info(
178
+ f"Chat history text for summarization ({len(chat_history_text.split())} words)"
179
+ )
180
+ response = await query(
181
+ {
182
+ "messages": [
183
+ {
184
+ "role": "user",
185
+ "content": chat_summarizer_prompt(chat_history_text),
186
+ }
187
+ ],
188
+ # "model": "Qwen/Qwen3-4B-Thinking-2507",
189
+ "model": "Qwen/Qwen2.5-Coder-32B-Instruct",
190
+ }
191
+ )
192
+
193
+ answer = response["choices"][0]["message"]
194
+ summary = extract_answer_content(answer["content"])
195
+ return summary
src/upgrade_advisor/chat/prompts.py CHANGED
@@ -1,29 +1,48 @@
1
- def result_package_summary_prompt(context, question, original_question="") -> str:
 
 
 
 
 
 
 
 
 
2
  return f"""Based on the following context from a package search, provide a
3
  concise summary of the key findings, issues, and recommendations. Focus on
4
- the most critical points that would help a developer understand the
5
  package and answers their question. Focus on the DEVELOPER QUESTION
6
- provided. LLM REWRITTEN QUESTION is provided for clarity, but the original
7
- intent should be prioritized.
 
8
  CONTEXT:
9
  {context}
10
- DEVELOPER QUESTION:
11
- {original_question}
12
- LLM REWRITTEN QUESTION:
13
- {question}
14
 
15
  SUMMARY:
16
  """
17
 
18
 
19
- def query_rewriter_prompt(original_question: str) -> str:
 
 
 
20
  return f"""
21
  You are a query rewriting agent that reformulates user questions
22
  about Python packages to be more specific and clear.
23
- You also aim to remove any typos in the text. You focus the
 
24
  typos made about the package metadata,
25
  versioning, repository or website URLs.
26
- This will help downstream agents provide more accurate and relevant answers.
 
 
 
 
 
 
 
27
 
28
  VERY IMPORTANT:
29
  - NEVER introduce new information that was not in the original question.
@@ -38,5 +57,30 @@ def query_rewriter_prompt(original_question: str) -> str:
38
  ORIGINAL QUESTION:
39
  {original_question}
40
 
 
 
 
41
  REWRITTEN QUESTION:
42
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def result_package_summary_prompt(
2
+ context, original_question, rewritten_question=None
3
+ ) -> str:
4
+ user_input = f"""
5
+ DEVELOPER QUESTION:
6
+ {original_question}
7
+ """
8
+ if rewritten_question:
9
+ user_input += f"\nREWRITTEN QUESTION (LLM-generated):\n{rewritten_question}\n"
10
+
11
  return f"""Based on the following context from a package search, provide a
12
  concise summary of the key findings, issues, and recommendations. Focus on
13
+ the main points that would help a developer understand the
14
  package and answers their question. Focus on the DEVELOPER QUESTION
15
+ provided. LLM REWRITTEN QUESTION is provided for clarity and to help you
16
+ better understand the intent, but always prioritize the original question.
17
+
18
  CONTEXT:
19
  {context}
20
+
21
+ {user_input}
 
 
22
 
23
  SUMMARY:
24
  """
25
 
26
 
27
+ def query_rewriter_prompt(original_question: str, summarized_history: str = "") -> str:
28
+ if not summarized_history:
29
+ summarized_history = "<NO PRIOR CHAT HISTORY>"
30
+ # construct the prompt
31
  return f"""
32
  You are a query rewriting agent that reformulates user questions
33
  about Python packages to be more specific and clear.
34
+ You also aim to remove any typos in the text.
35
+ You focus the
36
  typos made about the package metadata,
37
  versioning, repository or website URLs.
38
+ This will help downstream agents provide more accurate and relevant
39
+ answers.
40
+
41
+ ABOUT USING CHAT HISTORY:
42
+ Use the summarized chat history to provide context for rewriting.
43
+ Maybe the user has already asked related questions and you can use that
44
+ context to improve the question. If the history is not relevant, just focus
45
+ on improving the question itself.
46
 
47
  VERY IMPORTANT:
48
  - NEVER introduce new information that was not in the original question.
 
57
  ORIGINAL QUESTION:
58
  {original_question}
59
 
60
+ SUMMARIZED CHAT HISTORY:
61
+ {summarized_history}
62
+
63
  REWRITTEN QUESTION:
64
  """
65
+
66
+
67
+ def chat_summarizer_prompt(chat_history: str) -> str:
68
+ return f"""
69
+ You are a technical chat history summarization agent that condenses a conversation
70
+ between a user and an assistant about Python packages into a concise summary.
71
+ The summary should capture the main topics discussed, questions asked,
72
+ and any important context that would help understand the user's intent.
73
+
74
+ IMPORTANT GUIDELINES:
75
+ - Focus on specific packages, versions, issues discussed and any technical details.
76
+ - Emphasize any problems or challenges the user mentioned.
77
+ - Capture the user's goals or what they are trying to achieve.
78
+ - Omit any pleasantries or small talk; just focus on the technical content.
79
+ - Use bullet points or short sentences for clarity.
80
+ - Keep it brief and to the point, ideally under 100 words.
81
+
82
+ CHAT HISTORY:
83
+ {chat_history}
84
+
85
+ SUMMARY:
86
+ """