Spjimr / agent_py.py
shahidshaikh's picture
Upload 40 files
a52bae4 verified
# ============================================================================
# agent_py.py — Simple Python Agent backend (raw Mistral SDK tool-calling loop)
# ============================================================================
#
# CONTRACT: BACKEND_NAME, get_client, run, build_code_snippets
#
# PATTERN
# -------
# Classic tool-calling loop. The LLM sees the user's message plus a list
# of tool schemas. On each iteration it either:
# - emits tool calls (we run them and append results to the history), or
# - emits plain text (loop exits with that as the final reply).
#
# Bounded by MAX_AGENT_STEPS. No framework. Pure Python against the raw
# Mistral SDK.
# ============================================================================
import os
import json
# Defensive import — see agent_workflow.py for full explanation.
_Mistral = None
try:
from mistralai import Mistral as _Mistral # v1.x
except ImportError:
try:
from mistralai.client import Mistral as _Mistral # v2.x
except ImportError:
try:
from mistralai.client import MistralClient as _OldClient # v0.x
from mistralai.models.chat_completion import ChatMessage as _OldMsg
class _ChatShim:
def __init__(self, client):
self._client = client
def complete(self, model, messages, temperature=None,
max_tokens=None, tools=None):
msgs = [_OldMsg(role=m["role"], content=m.get("content", ""))
for m in messages]
return self._client.chat(
model=model, messages=msgs,
temperature=temperature, max_tokens=max_tokens,
tools=tools,
)
class _MistralV0Wrapper:
def __init__(self, api_key):
self._client = _OldClient(api_key=api_key)
self.chat = _ChatShim(self._client)
_Mistral = _MistralV0Wrapper
except ImportError as _e:
raise ImportError(
"mistralai package is missing or an unknown version. "
f"Last error: {_e}"
)
Mistral = _Mistral
from parameters import TEMPERATURE, MAX_TOKENS, MAX_AGENT_STEPS
from prompts import AGENT_SYSTEM
from tools import TOOL_FUNCTIONS, TOOL_SCHEMAS
import providers
BACKEND_NAME = "Simple Python Agent"
def get_client(api_key, provider="Mistral"):
return providers.get_llm_client(provider, api_key)
def _llm(client, messages, tools=None, provider="Mistral"):
model = providers.get_llm_model(provider)
return client.chat.complete(
model=model,
temperature=TEMPERATURE,
max_tokens=MAX_TOKENS,
messages=messages,
tools=tools,
).choices[0].message
def run(client, user_message, provider="Mistral"):
"""Tool-calling loop. LLM decides when to stop."""
messages = [
{"role": "system", "content": AGENT_SYSTEM},
{"role": "user", "content": user_message},
]
steps = []
tool_calls_made = []
for step_num in range(1, MAX_AGENT_STEPS + 1):
msg = _llm(client, messages, tools=TOOL_SCHEMAS, provider=provider)
messages.append(providers.serialize_assistant_message(msg, provider))
tool_calls = msg.tool_calls or []
if not tool_calls:
# No more tool calls — model has a final answer
steps.append({
"step": step_num, "type": "final", "tool": "-",
"args": "-", "result": msg.content or "",
})
return {
"reply": msg.content or "",
"steps": steps,
"extracted": {"tool_calls_made": tool_calls_made},
}
# Execute each tool call and append results
for tc in tool_calls:
name = tc.function.name
args_raw = tc.function.arguments
args = json.loads(args_raw) if isinstance(args_raw, str) else args_raw
result = TOOL_FUNCTIONS[name](**args)
steps.append({
"step": step_num, "type": "tool_call", "tool": name,
"args": json.dumps(args), "result": str(result),
})
tool_calls_made.append({"tool": name, "args": args, "result": result})
messages.append(providers.serialize_tool_result(tc, name, result, provider))
steps.append({
"step": MAX_AGENT_STEPS, "type": "limit", "tool": "-",
"args": "-", "result": "max steps reached",
})
return {
"reply": "(max agent steps reached)",
"steps": steps,
"extracted": {"tool_calls_made": tool_calls_made},
}
def build_code_snippets(user_message, steps):
lines = [
"# Backend: Simple Python Agent",
"# Raw Mistral SDK tool-calling loop. No framework.",
f"# User message: {user_message}",
"",
"messages = [",
" {'role': 'system', 'content': AGENT_SYSTEM},",
f" {{'role': 'user', 'content': {user_message!r}}},",
"]",
"",
"for step in range(1, MAX_AGENT_STEPS + 1):",
" msg = client.chat.complete(",
" model=MODEL, messages=messages, tools=TOOL_SCHEMAS",
" ).choices[0].message",
" messages.append(msg.model_dump(exclude_none=True))",
"",
" if not msg.tool_calls:",
" break # plain-text reply means we are done",
"",
" for tc in msg.tool_calls:",
" name = tc.function.name",
" args = json.loads(tc.function.arguments)",
" result = TOOL_FUNCTIONS[name](**args)",
" messages.append({",
" 'role': 'tool', 'name': name,",
" 'content': result, 'tool_call_id': tc.id,",
" })",
"",
"# ---------- actual step log ----------",
]
for s in steps:
lines.append(f"# Step {s['step']} [{s['type']}] tool={s['tool']}")
lines.append(f"# args: {s['args']}")
lines.append(f"# result: {s['result']}")
return "\n".join(lines)