Spaces:
Running
Running
Geevarghese George
commited on
Commit
·
c5964f7
1
Parent(s):
a4eaa0c
flow established
Browse files- app.py +52 -90
- config.py +3 -0
- poetry.lock +319 -10
- pyproject.toml +1 -0
- src/upgrade_advisor/agents/package.py +19 -118
- src/upgrade_advisor/agents/prompts.py +17 -16
- src/upgrade_advisor/chat/chat.py +134 -7
- src/upgrade_advisor/chat/prompts.py +55 -11
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
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
# Collect events from the agent run
|
| 90 |
-
events =
|
|
|
|
|
|
|
| 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(
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 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],
|
| 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 = "
|
| 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.
|
| 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.
|
| 2511 |
-
{file = "smolagents-1.
|
| 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 = "
|
|
|
|
| 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
|
| 5 |
|
| 6 |
from pydantic import BaseModel
|
| 7 |
-
from smolagents import CodeAgent
|
| 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(
|
|
|
|
|
|
|
| 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 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 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 |
-
|
| 191 |
"name": "unknown",
|
| 192 |
"version": "unknown",
|
| 193 |
"summary": "Error occurred: " + str(e),
|
| 194 |
}
|
| 195 |
|
| 196 |
-
def discover_package_info(
|
|
|
|
|
|
|
| 197 |
"""Public method to start package discovery."""
|
| 198 |
-
return self._discover_package_info(
|
|
|
|
|
|
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
-
|
| 89 |
-
|
| 90 |
-
from
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,
|
| 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 |
-
|
| 41 |
),
|
| 42 |
}
|
| 43 |
],
|
|
@@ -50,13 +60,87 @@ async def run_document_qa(
|
|
| 50 |
return extract_answer_content(answer["content"])
|
| 51 |
|
| 52 |
|
| 53 |
-
async def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
response = await query(
|
| 55 |
{
|
| 56 |
"messages": [
|
| 57 |
{
|
| 58 |
"role": "user",
|
| 59 |
-
"content": query_rewriter_prompt(
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 5 |
package and answers their question. Focus on the DEVELOPER QUESTION
|
| 6 |
-
provided. LLM REWRITTEN QUESTION is provided for clarity
|
| 7 |
-
intent
|
|
|
|
| 8 |
CONTEXT:
|
| 9 |
{context}
|
| 10 |
-
|
| 11 |
-
{
|
| 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.
|
|
|
|
| 24 |
typos made about the package metadata,
|
| 25 |
versioning, repository or website URLs.
|
| 26 |
-
This will help downstream agents provide more accurate and relevant
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
"""
|