fix: tool calls, again lol

This commit is contained in:
Shivering Ocelot committed 2026-10-10 13:31:23 -05:00
1 parent 0dee6a392f
commit f217e7db3e
3 files changed
+67 -23

No files matched your search

+6 -7
View File
@@ -31,17 +31,17 @@ async def chat_completion_loop(app, run, messages, model_id=None, tools=None):
opts=run.input, opts=run.input,
) )
pre_len = len(messages)
async for msg in generator: async for msg in generator:
messages.append(msg) messages.append(msg)
run.emit_event('inference', msg)
if msg.role == 'tool': if msg.role == 'tool':
tool_calls.append(msg.content) tool_calls.append(msg.content)
if msg.role == 'assistant': if msg.role == 'assistant':
turn_response = True turn_response = True
run.emit_event('inference', msg) # Avoid infinite loops from server failures
if len(messages) == pre_len: break
# Broken generator with no output
else: break
for tc in tool_calls: for tc in tool_calls:
tool = next((t for t in tools if t.name == tc.tool_name), None) tool = next((t for t in tools if t.name == tc.tool_name), None)
@@ -53,9 +53,8 @@ async def chat_completion_loop(app, run, messages, model_id=None, tools=None):
else: else:
tc.tool = tool tc.tool = tool
# If not calling must output something useful # If not calling, respond to end the turn
else: if not (tool_calls and execute_tools) and turn_response: break
if turn_response: break
run.close() run.close()
+60 -15
View File
@@ -6,6 +6,7 @@ class's automatic ``ToolSpec`` generation (docstring -> description,
""" """
import re import re
import urllib.parse
import urllib.request import urllib.request
from html import unescape from html import unescape
@@ -28,24 +29,68 @@ def _strip_html(html: str) -> str:
class WebSearch(Tool): class WebSearch(Tool):
"""Search the web with DuckDuckGo. Returns titles, URLs and snippets.""" """Search the web with DuckDuckGo. Returns titles, URLs and snippets."""
ENDPOINT = "https://html.duckduckgo.com/html/"
USER_AGENT = (
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/124.0 Safari/537.36"
)
# Result anchors look like:
# <a class="result__a" href="//duckduckgo.com/l/?uddg=...">Title</a>
_RESULT_RE = re.compile(
r'<a[^>]*class="[^"]*\bresult__a\b[^"]*"[^>]*href="(?P<href>[^"]+)"[^>]*>'
r"(?P<title>.*?)</a>",
re.DOTALL,
)
_SNIPPET_RE = re.compile(
r'<a[^>]*class="[^"]*\bresult__snippet\b[^"]*"[^>]*>(?P<snippet>.*?)</a>',
re.DOTALL,
)
def run(self, query: str, max_results: int = 5): def run(self, query: str, max_results: int = 5):
# `ddgs` is the current package name; `duckduckgo_search` is the legacy one. body = urllib.parse.urlencode({"q": query}).encode("utf-8")
try: req = urllib.request.Request(
from ddgs import DDGS self.ENDPOINT,
except ImportError: # pragma: no cover - fallback for older installs data=body,
from duckduckgo_search import DDGS method="POST",
headers={
"User-Agent": self.USER_AGENT,
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "text/html",
},
)
with urllib.request.urlopen(req, timeout=15) as resp:
page = resp.read().decode("utf-8", errors="replace")
with DDGS() as ddgs: snippets = [m.group("snippet") for m in self._SNIPPET_RE.finditer(page)]
results = list(ddgs.text(query, max_results=max_results))
return [ results = []
{ for i, match in enumerate(self._RESULT_RE.finditer(page)):
"title": r.get("title", ""), if len(results) >= max_results:
"url": r.get("href", ""), break
"snippet": r.get("body", ""), results.append({
} "title": self._clean(match.group("title")),
for r in results "url": self._unwrap(match.group("href")),
] "snippet": self._clean(snippets[i]) if i < len(snippets) else "",
})
return results
@staticmethod
def _clean(value: str) -> str:
return unescape(re.sub(r"<[^>]+>", "", value)).strip()
@staticmethod
def _unwrap(href: str) -> str:
# DDG wraps result links as //duckduckgo.com/l/?uddg=<url-encoded-target>
if "uddg=" in href:
target = urllib.parse.parse_qs(urllib.parse.urlparse(href).query).get(
"uddg", [""]
)[0]
if target:
return target
if href.startswith("//"):
return "https:" + href
return href
class WebFetch(Tool): class WebFetch(Tool):
+1 -1
View File
@@ -149,7 +149,7 @@ class Tool:
@classmethod @classmethod
def tool_name(cls): def tool_name(cls):
return ''.join(['-'+c.lower() if c.isupper() else c for c in cls.__name__]).lstrip('_') return ''.join(['-'+c.lower() if c.isupper() else c for c in cls.__name__]).lstrip('-')
def _generate_spec(self): def _generate_spec(self):