Spaces:
Sleeping
Sleeping
Download llm.py from AndreaNotes1/ayn: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/spaces/AndreaNotes1/ayn/resolve/main/llm.py
- Command line
-
hf download hf://spaces/AndreaNotes1/ayn/llm.py
-
curl -L -o llm.py https://huggingface.co/spaces/AndreaNotes1/ayn/resolve/main/llm.py
4.34 kB
| """ | |
| LLM provider abstraction with tool-calling support (live web search). | |
| Launch on the free provider (Groq). When you're ready to upgrade, set | |
| LLM_PROVIDER=anthropic and ANTHROPIC_API_KEY - nothing else needs to change. | |
| Note: web-search tool-calling is currently wired up for the Groq path only; | |
| the Anthropic path still works, just without live search for now. | |
| """ | |
| import json | |
| from config import settings | |
| from search import WEB_SEARCH_TOOL_SCHEMA, search_web | |
| _groq_client = None | |
| _anthropic_client = None | |
| MAX_TOOL_ITERATIONS = 3 | |
| def _get_groq(): | |
| global _groq_client | |
| if _groq_client is None: | |
| from groq import Groq | |
| if not settings.GROQ_API_KEY: | |
| raise RuntimeError( | |
| "GROQ_API_KEY is not set. Get a free key at https://console.groq.com/keys" | |
| ) | |
| _groq_client = Groq(api_key=settings.GROQ_API_KEY) | |
| return _groq_client | |
| def _get_anthropic(): | |
| global _anthropic_client | |
| if _anthropic_client is None: | |
| import anthropic | |
| if not settings.ANTHROPIC_API_KEY: | |
| raise RuntimeError("ANTHROPIC_API_KEY is not set.") | |
| _anthropic_client = anthropic.Anthropic(api_key=settings.ANTHROPIC_API_KEY) | |
| return _anthropic_client | |
| async def generate_reply(system_prompt: str, messages: list[dict]) -> tuple[str, bool]: | |
| """ | |
| messages: list of {"role": "user"|"assistant", "content": str}, oldest first. | |
| Returns (reply_text, used_web_search). | |
| """ | |
| provider = settings.LLM_PROVIDER.lower() | |
| if provider == "groq": | |
| return await _generate_groq(system_prompt, messages) | |
| if provider == "anthropic": | |
| client = _get_anthropic() | |
| resp = client.messages.create( | |
| model=settings.ANTHROPIC_MODEL, | |
| max_tokens=500, | |
| system=system_prompt, | |
| messages=messages, | |
| ) | |
| text = "".join(b.text for b in resp.content if b.type == "text").strip() | |
| return text, False | |
| raise RuntimeError(f"Unknown LLM_PROVIDER: '{provider}'. Use 'groq' or 'anthropic'.") | |
| async def _generate_groq(system_prompt: str, messages: list[dict]) -> tuple[str, bool]: | |
| client = _get_groq() | |
| api_messages = [{"role": "system", "content": system_prompt}] + list(messages) | |
| tools_enabled = bool(settings.TAVILY_API_KEY) | |
| used_web_search = False | |
| for _ in range(MAX_TOOL_ITERATIONS): | |
| kwargs = dict( | |
| model=settings.GROQ_MODEL, | |
| messages=api_messages, | |
| max_tokens=500, | |
| temperature=0.7, | |
| ) | |
| if tools_enabled: | |
| kwargs["tools"] = [WEB_SEARCH_TOOL_SCHEMA] | |
| kwargs["tool_choice"] = "auto" | |
| resp = client.chat.completions.create(**kwargs) | |
| message = resp.choices[0].message | |
| if not message.tool_calls: | |
| return (message.content or "").strip(), used_web_search | |
| # The model wants to call a tool - append its request, then the result(s). | |
| api_messages.append( | |
| { | |
| "role": "assistant", | |
| "content": message.content or "", | |
| "tool_calls": [ | |
| { | |
| "id": tc.id, | |
| "type": "function", | |
| "function": {"name": tc.function.name, "arguments": tc.function.arguments}, | |
| } | |
| for tc in message.tool_calls | |
| ], | |
| } | |
| ) | |
| for tc in message.tool_calls: | |
| if tc.function.name == "search_web": | |
| args = json.loads(tc.function.arguments or "{}") | |
| result_text = await search_web(args.get("query", "")) | |
| used_web_search = True | |
| else: | |
| result_text = f"Unknown tool: {tc.function.name}" | |
| api_messages.append( | |
| { | |
| "role": "tool", | |
| "tool_call_id": tc.id, | |
| "content": result_text, | |
| } | |
| ) | |
| # Ran out of tool-calling rounds - ask once more without tools to force | |
| # a final answer rather than looping forever. | |
| resp = client.chat.completions.create( | |
| model=settings.GROQ_MODEL, | |
| messages=api_messages, | |
| max_tokens=500, | |
| temperature=0.7, | |
| ) | |
| return (resp.choices[0].message.content or "").strip(), used_web_search | |