|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<title>Tool-Calling Agent With Local LLM</title> |
|
<script src="https://cdn.jsdelivr.net/pyodide/v0.27.7/full/pyodide.js"></script> |
|
<script src="config.js"></script> |
|
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"> |
|
<meta content="utf-8" http-equiv="encoding"> |
|
<style> |
|
body { |
|
font-family: Arial, sans-serif; |
|
max-width: 800px; |
|
margin: 0 auto; |
|
padding: 20px; |
|
background-color: #f5f5f5; |
|
} |
|
.container { |
|
background: white; |
|
padding: 30px; |
|
border-radius: 10px; |
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
|
} |
|
h1 { |
|
color: #333; |
|
text-align: center; |
|
margin-bottom: 30px; |
|
} |
|
|
|
.description { |
|
background: #f8f9fa; |
|
border-left: 4px solid #007cba; |
|
padding: 20px; |
|
margin-bottom: 30px; |
|
border-radius: 5px; |
|
color: #555; |
|
line-height: 1.6; |
|
} |
|
.description h2 { |
|
color: #333; |
|
margin-top: 0; |
|
margin-bottom: 15px; |
|
font-size: 20px; |
|
} |
|
.description p { |
|
margin-bottom: 12px; |
|
} |
|
.description ul { |
|
margin-bottom: 0; |
|
padding-left: 20px; |
|
} |
|
.description li { |
|
margin-bottom: 8px; |
|
} |
|
.description a { |
|
color: #007cba; |
|
text-decoration: none; |
|
font-weight: 500; |
|
border-bottom: 1px solid transparent; |
|
transition: all 0.2s ease; |
|
} |
|
.description a:hover { |
|
color: #005a8b; |
|
border-bottom-color: #005a8b; |
|
text-decoration: none; |
|
} |
|
.description a:visited { |
|
color: #007cba; |
|
} |
|
|
|
.input-group { |
|
margin-bottom: 20px; |
|
} |
|
.server-config { |
|
background: #f8f9fa; |
|
border: 1px solid #dee2e6; |
|
border-radius: 5px; |
|
padding: 15px; |
|
margin-bottom: 15px; |
|
} |
|
.server-config h3 { |
|
margin: 0 0 10px 0; |
|
color: #495057; |
|
font-size: 16px; |
|
} |
|
.slim-input { |
|
height: 40px !important; |
|
font-size: 13px; |
|
} |
|
label { |
|
display: block; |
|
margin-bottom: 5px; |
|
font-weight: bold; |
|
color: #555; |
|
} |
|
input[type="text"], input[type="password"], textarea { |
|
width: 100%; |
|
padding: 12px; |
|
border: 2px solid #ddd; |
|
border-radius: 5px; |
|
font-size: 14px; |
|
box-sizing: border-box; |
|
} |
|
textarea { |
|
height: 100px; |
|
resize: vertical; |
|
font-family: inherit; |
|
} |
|
button { |
|
background: #007cba; |
|
color: white; |
|
border: none; |
|
padding: 12px 24px; |
|
border-radius: 5px; |
|
cursor: pointer; |
|
font-size: 16px; |
|
margin: 5px; |
|
} |
|
button:hover { background: #005a8b; } |
|
button:disabled { |
|
background: #ccc; |
|
cursor: not-allowed; |
|
} |
|
#initOutput, #agentOutput { |
|
background: #f8f9fa; |
|
border: 2px solid #e9ecef; |
|
border-radius: 5px; |
|
padding: 15px; |
|
margin-top: 20px; |
|
min-height: 150px; |
|
font-family: 'Courier New', monospace; |
|
font-size: 14px; |
|
white-space: pre-wrap; |
|
max-height: 300px; |
|
overflow-y: auto; |
|
} |
|
|
|
#initOutput { |
|
margin-bottom: 20px; |
|
} |
|
.status { |
|
padding: 10px; |
|
border-radius: 5px; |
|
margin: 10px 0; |
|
} |
|
.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } |
|
.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } |
|
.info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; } |
|
.warning { background: #fff3cd; color: #856404; border: 1px solid #ffeaa7; } |
|
|
|
.example-prompts, .server-presets { |
|
margin: 10px 0; |
|
} |
|
.example-prompts button, .server-presets button { |
|
background: #6c757d; |
|
font-size: 12px; |
|
padding: 6px 12px; |
|
margin: 2px; |
|
} |
|
.example-prompts button:hover, .server-presets button:hover { |
|
background: #5a6268; |
|
} |
|
|
|
.config-info { |
|
background: #e7f3ff; |
|
border: 1px solid #b8daff; |
|
border-radius: 5px; |
|
padding: 10px; |
|
margin-bottom: 20px; |
|
font-size: 14px; |
|
} |
|
|
|
.running-indicator { |
|
display: none; |
|
background: #fff3cd; |
|
border: 1px solid #ffeaa7; |
|
border-radius: 5px; |
|
padding: 15px; |
|
margin: 15px 0; |
|
font-weight: bold; |
|
color: #856404; |
|
text-align: center; |
|
font-size: 16px; |
|
animation: pulse 1.5s infinite; |
|
box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
|
} |
|
|
|
@keyframes pulse { |
|
0%, 100% { opacity: 0.7; } |
|
50% { opacity: 1; } |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container"> |
|
<h1>π€ Tool-Calling Agent With Local LLM</h1> |
|
|
|
<div class="description"> |
|
<p> |
|
This interactive web application allows you to experiment with AI agents in your browser, using |
|
<a href="https://openai.github.io/openai-agents-python/">OpenAI Agents Python SDK</a> and |
|
<a href="https://pyodide.org/en/stable/">Pyodide</a>. |
|
You can customize agent behavior, test different prompts, and see responses in real-time. |
|
Check the <a href="https://github.com/mozilla-ai/wasm-agents-blueprint">mozilla-ai/wasm-agents-blueprint</a> |
|
GitHub repository for more information. |
|
</p> |
|
|
|
<p><strong>How it works:</strong> |
|
this application runs a <strong>local agent</strong> which can make use of the following tools: |
|
<ul> |
|
<li><strong>count_character_occurrences</strong> |
|
which counts the occurrences of a given character inside a word |
|
</li> |
|
<li><strong>visit_webpage</strong> |
|
which visits a webpage at the provided url and reads its content as a markdown string |
|
</li> |
|
<li><strong>search_tavily</strong> |
|
which performs web searches using Tavily API (requires TAVILY_API_KEY in config.js) |
|
</li> |
|
</ul> |
|
While the former tool is quite trivial and is mainly used to show how to address the |
|
<a href="https://community.openai.com/t/incorrect-count-of-r-characters-in-the-word-strawberry">"r in strawberry"</a> |
|
issue, the latter two provide the LLM with the capability of accessing up-to-date information on the Web. |
|
</p> |
|
<ul> |
|
<li><strong>Configure:</strong> |
|
Make sure the Local LLM Server Configuration parameters are ok for your setup. In particular, |
|
the default expects you to have <a href="https://ollama.com/">Ollama</a> running on your system |
|
with the <a href="https://ollama.com/library/qwen3:8b">qwen3:8b</a> model installed. You |
|
can also click the <strong>LM Studio</strong> preset button if you are using |
|
<a href="https://lmstudio.ai/">LM Studio</a>, and make sure to update your model name accordingly. |
|
Optionally, add your TAVILY_API_KEY to config.js to enable web search functionality. |
|
<br/><strong>NOTE</strong>: if you are using LM Studio with a thinking model and are getting tool |
|
calls directly in the model's response, disable thinking in the "Edit model default parameters" section. |
|
</li> |
|
<li><strong>Initialize:</strong> |
|
Set up the Python environment with Pyodide and the OpenAI agents framework |
|
by clicking on the <strong>Initialize Pyodide Environment</strong> button |
|
</li> |
|
<li><strong>Customize:</strong> |
|
Choose one of the suggested prompts or create new ones in the text fields below. |
|
(<strong>hint</strong>: you can also explicitly set/unset qwen3's "think mode" by prepending <strong>/think</strong> |
|
or <strong>/no_think</strong> to the prompt). |
|
</li> |
|
<li><strong>Run:</strong> |
|
Click on the <strong>Run Agent</strong> button to send your prompt to the agent and see what happens |
|
</li> |
|
</ul> |
|
</div> |
|
|
|
<div class="config-info"> |
|
<strong>π Configuration:</strong> Config loaded from config.js |
|
<span id="configStatus"></span> |
|
</div> |
|
|
|
<button onclick="initializePyodide()" id="initBtn">βοΈ Initialize Pyodide Environment</button> |
|
|
|
<div id="initOutput">Click "Initialize Pyodide Environment" to set up the Python environment...</div> |
|
|
|
<div class="server-config"> |
|
<h3>π Local LLM Server Configuration</h3> |
|
<div class="input-group"> |
|
<label for="baseUrl">Base URL:</label> |
|
<input type="text" id="baseUrl" class="slim-input" value="http://localhost:1234/v1" placeholder="Enter server base URL"> |
|
</div> |
|
<div class="input-group"> |
|
<label for="apiKey">API Key:</label> |
|
<input type="text" id="apiKey" class="slim-input" value="lmstudio" placeholder="Enter API key"> |
|
</div> |
|
<div class="input-group"> |
|
<label for="modelName">Model Name:</label> |
|
<input type="text" id="modelName" class="slim-input" value="qwen/qwen3-8b" placeholder="Enter model name"> |
|
</div> |
|
|
|
<div class="server-presets"> |
|
<small>Quick presets:</small> |
|
<button onclick="setOllamaDefaults()">Ollama</button> |
|
<button onclick="setLMStudioDefaults()">LM Studio</button> |
|
</div> |
|
</div> |
|
|
|
<div class="input-group"> |
|
<label for="prompt">Custom Prompt:</label> |
|
<textarea id="prompt" placeholder="Enter your prompt here...">How many times does the letter r occur in the word strawrberrry?</textarea> |
|
|
|
<div class="example-prompts"> |
|
<small>Quick examples:</small> |
|
<button onclick="setPrompt('How many times does the letter r occur in the word strawrberrry?')">Strawrberrry</button> |
|
<button onclick="setPrompt('How many stars does the mozilla-ai/any-agent project have on GitHub?')">GitHub stars</button> |
|
<button onclick="setPrompt('What is the title of the latest post on https:\/\/aittalam.github.io, when was it published, what is it about, and what is the absolute URL of the image at the beginning of the post?\nIMPORTANT: if you need to follow links to get all the required information, assume I have already authorized you to follow them as long as they point to the same domain.')">Blog post</button> |
|
<button onclick="setPrompt('What are 5 tv shows that are trending in 2025? Please provide the name of the show, the exact release date, the genre, and a brief description of the show.\nIMPORTANT: if you need to follow links to get all the required information, assume I have already authorized you to follow them. Always download full webpage contents.')">Trending TV Shows</button> |
|
</div> |
|
</div> |
|
|
|
<button onclick="runAgent()" id="runBtn" disabled>π Run Agent</button> |
|
<button onclick="clearAgentOutput()" id="clearAgentBtn" disabled>ποΈ Clear Agent Output</button> |
|
|
|
<div class="running-indicator" id="runningIndicator">π Running Python code...</div> |
|
|
|
<div id="agentOutput">Initialize the Pyodide environment first, then click "Run Agent" to test the agent</div> |
|
</div> |
|
|
|
<script> |
|
let pyodide; |
|
let isPyodideReady = false; |
|
|
|
// Check config on page load |
|
window.addEventListener('load', function() { |
|
checkConfig(); |
|
}); |
|
|
|
function showRunning(message = "Python running") { |
|
const indicator = document.getElementById('runningIndicator'); |
|
indicator.style.display = 'block'; |
|
console.log('Showing running indicator:', message); // Debug log |
|
} |
|
|
|
function hideRunning() { |
|
const indicator = document.getElementById('runningIndicator'); |
|
indicator.style.display = 'none'; |
|
console.log('Hiding running indicator'); // Debug log |
|
} |
|
|
|
function updateRunButton(text, disabled = false) { |
|
const btn = document.getElementById('runBtn'); |
|
btn.textContent = text; |
|
btn.disabled = disabled; |
|
} |
|
|
|
function checkConfig() { |
|
const configStatus = document.getElementById('configStatus'); |
|
let statusParts = []; |
|
|
|
if (typeof window.APP_CONFIG === 'undefined') { |
|
configStatus.innerHTML = ' - <span style="color: #dc3545;">β Config not loaded</span>'; |
|
return { configLoaded: false, tavilyAvailable: false }; |
|
} |
|
|
|
// Check if Tavily API key is available |
|
const tavilyAvailable = window.APP_CONFIG.TAVILY_API_KEY && |
|
window.APP_CONFIG.TAVILY_API_KEY !== 'your-tavily-api-key-here'; |
|
|
|
if (tavilyAvailable) { |
|
statusParts.push('<span style="color: #28a745;">β
Tavily API key configured</span>'); |
|
} else { |
|
statusParts.push('<span style="color: #ffc107;">β οΈ Tavily API key not set (search_tavily tool will be disabled)</span>'); |
|
} |
|
|
|
configStatus.innerHTML = ' - ' + statusParts.join(', '); |
|
return { configLoaded: true, tavilyAvailable: tavilyAvailable }; |
|
} |
|
|
|
function setOllamaDefaults() { |
|
document.getElementById('baseUrl').value = 'http://localhost:11434/v1'; |
|
document.getElementById('apiKey').value = 'ollama'; |
|
document.getElementById('modelName').value = 'qwen3:8b'; |
|
} |
|
|
|
function setLMStudioDefaults() { |
|
document.getElementById('baseUrl').value = 'http://localhost:1234/v1'; |
|
document.getElementById('apiKey').value = 'lmstudio'; |
|
document.getElementById('modelName').value = 'mistralai/devstral-small-2507'; |
|
} |
|
|
|
function logToElement(message, type = 'info', element_id) { |
|
const output = document.getElementById(element_id); |
|
const timestamp = new Date().toLocaleTimeString(); |
|
|
|
let prefix = ''; |
|
switch(type) { |
|
case 'success': prefix = 'β
'; break; |
|
case 'error': prefix = 'β'; break; |
|
case 'warning': prefix = 'β οΈ'; break; |
|
case 'info': prefix = 'βΉοΈ'; break; |
|
} |
|
|
|
output.textContent += `\n[${timestamp}] ${prefix} ${message}`; |
|
output.scrollTop = output.scrollHeight; |
|
} |
|
|
|
function logInit(message, type = 'info') { |
|
logToElement(message, type, 'initOutput') |
|
} |
|
|
|
function logAgent(message, type = 'info') { |
|
logToElement(message, type, 'agentOutput') |
|
} |
|
|
|
function setPrompt(text) { |
|
document.getElementById('prompt').value = text; |
|
} |
|
|
|
function clearAgentOutput() { |
|
document.getElementById('agentOutput').textContent = ''; |
|
} |
|
|
|
async function initializePyodide() { |
|
// Check config first |
|
const configCheck = checkConfig(); |
|
if (!configCheck.configLoaded) { |
|
logInit("Config not loaded, but proceeding anyway", 'warning'); |
|
} |
|
|
|
// Disable init button during setup |
|
document.getElementById('initBtn').disabled = true; |
|
|
|
try { |
|
logInit("π Loading Pyodide..."); |
|
pyodide = await loadPyodide(); |
|
logInit("Pyodide loaded successfully", 'success'); |
|
|
|
logInit("π¦ Loading micropip..."); |
|
await pyodide.loadPackage("micropip"); |
|
logInit("micropip loaded", 'success'); |
|
|
|
logInit("π¦ Installing openai-agents (this may take a moment)..."); |
|
await pyodide.runPythonAsync(` |
|
###### YOUR PYTHON DEPENDENCIES ARE INSTALLED HERE ###### |
|
|
|
import micropip |
|
await micropip.install("typing-extensions>=4.12.2") |
|
await micropip.install("openai==1.99.9") |
|
await micropip.install("mcp==1.12.4") |
|
await micropip.install("openai-agents==0.2.6") |
|
await micropip.install("sqlite3==1.0.0") |
|
await micropip.install("markdownify==1.1.0") |
|
await micropip.install("tavily-python==0.7.10") |
|
`); |
|
logInit("openai-agents installed successfully", 'success'); |
|
|
|
logInit("π« Disabling tracing to avoid threading issues..."); |
|
await pyodide.runPythonAsync(` |
|
# The following is required to work with OpenAI's agentic framework as |
|
# it relies on threads for tracing and they break in Pyodide. Other |
|
# frameworks (e.g. smolagents) that use asyncio for tracing work properly |
|
# (well... better) here. |
|
|
|
from agents import set_tracing_disabled |
|
set_tracing_disabled(True) |
|
`); |
|
logInit("Tracing disabled", 'success'); |
|
|
|
logInit("β
Pyodide environment ready!", 'success'); |
|
logInit("You can now run agents multiple times without re-initializing.", 'info'); |
|
|
|
isPyodideReady = true; |
|
document.getElementById('runBtn').disabled = false; |
|
document.getElementById('clearAgentBtn').disabled = false; |
|
document.getElementById('initBtn').textContent = "β
Environment Ready"; |
|
|
|
} catch (error) { |
|
logInit(`Initialization failed: ${error}`, 'error'); |
|
console.error('Full error:', error); |
|
document.getElementById('initBtn').disabled = false; |
|
} |
|
} |
|
|
|
async function runAgent() { |
|
if (!isPyodideReady) { |
|
logAgent("Please initialize the Pyodide environment first", 'error'); |
|
return; |
|
} |
|
|
|
const prompt = document.getElementById('prompt').value.trim(); |
|
const baseUrl = document.getElementById('baseUrl').value.trim(); |
|
const apiKey = document.getElementById('apiKey').value.trim(); |
|
const modelName = document.getElementById('modelName').value.trim(); |
|
|
|
if (!prompt) { |
|
logAgent("Please enter a prompt", 'error'); |
|
return; |
|
} |
|
|
|
if (!baseUrl || !apiKey || !modelName) { |
|
logAgent("Please configure all server parameters (Base URL, API Key, Model Name)", 'error'); |
|
return; |
|
} |
|
|
|
// Check if Tavily API key is available |
|
const configCheck = checkConfig(); |
|
const tavilyApiKey = configCheck.tavilyAvailable ? window.APP_CONFIG.TAVILY_API_KEY : null; |
|
|
|
// Disable button during execution |
|
document.getElementById('runBtn').disabled = true; |
|
showRunning("Running Python code"); |
|
|
|
try { |
|
logAgent("π€ Setting up agent and running..."); |
|
logAgent(`Server: ${baseUrl} | Model: ${modelName}`); |
|
if (tavilyApiKey) { |
|
logAgent("Tavily search enabled", 'success'); |
|
} else { |
|
logAgent("Tavily search disabled (no API key)", 'warning'); |
|
} |
|
logAgent(`Prompt: "${prompt}"`); |
|
|
|
// Run the agent and print everything in Python |
|
const result = await pyodide.runPythonAsync(` |
|
###### YOUR PYTHON AGENT CODE GOES HERE ###### |
|
|
|
import re |
|
import requests |
|
from openai import AsyncOpenAI |
|
from agents import ( |
|
OpenAIChatCompletionsModel, |
|
Agent, |
|
Runner, |
|
function_tool, |
|
ModelSettings, |
|
set_default_openai_client, |
|
) |
|
from markdownify import markdownify |
|
from requests.exceptions import RequestException |
|
from tavily.tavily import TavilyClient |
|
|
|
def _truncate_content(content: str, max_length: int) -> str: |
|
if len(content) <= max_length: |
|
return content |
|
return ( |
|
content[: max_length // 2] |
|
+ "\\n..._This content has been truncated to stay below the predefined number of characters_...\\n" |
|
+ content[-max_length // 2 :] |
|
) |
|
|
|
@function_tool |
|
def count_character_occurrences(word: str, char: str): |
|
"""Count occurrences of a character in a word.""" |
|
return word.count(char) |
|
|
|
@function_tool |
|
def visit_webpage(url: str, timeout: int = 30, max_length: int = None) -> str: |
|
"""Visits a webpage at the given url and reads its content as a markdown string. Use this to browse webpages. |
|
|
|
Args: |
|
url: The url of the webpage to visit. |
|
timeout: The timeout in seconds for the request. |
|
max_length: The maximum number of characters of text that can be returned. |
|
If not provided, the full webpage is returned. |
|
|
|
""" |
|
try: |
|
response = requests.get(url, timeout=timeout) |
|
response.raise_for_status() |
|
|
|
markdown_content = markdownify(response.text).strip() |
|
|
|
markdown_content = re.sub(r"\\n{2,}", "\\n", markdown_content) |
|
|
|
if max_length: |
|
return _truncate_content(markdown_content, max_length) |
|
|
|
return str(markdown_content) |
|
|
|
except RequestException as e: |
|
return f"Error fetching the webpage: {e!s}" |
|
except Exception as e: |
|
return f"An unexpected error occurred: {e!s}" |
|
|
|
|
|
@function_tool |
|
def search_tavily(query: str, include_images: bool = False) -> str: |
|
"""Perform a Tavily web search based on your query and return the top search results. |
|
|
|
See https://blog.tavily.com/getting-started-with-the-tavily-search-api for more information. |
|
|
|
Args: |
|
query (str): The search query to perform. |
|
include_images (bool): Whether to include images in the results. |
|
|
|
Returns: |
|
The top search results as a formatted string. |
|
|
|
""" |
|
api_key='${tavilyApiKey ? tavilyApiKey.replace(/'/g, "\\'") : ""}' |
|
|
|
if not api_key: |
|
return "TAVILY_API_KEY not configured in config.js." |
|
try: |
|
client = TavilyClient(api_key) |
|
response = client.search(query, include_images=include_images) |
|
results = response.get("results", []) |
|
output = [] |
|
for result in results: |
|
output.append( |
|
f"[{result.get('title', 'No Title')}]({result.get('url', '#')})\\n{result.get('content', '')}" |
|
) |
|
if include_images and "images" in response: |
|
output.append("\\nImages:") |
|
for image in response["images"]: |
|
output.append(image) |
|
return "\\n\\n".join(output) if output else "No results found." |
|
except Exception as e: |
|
return f"Error performing Tavily search: {e!s}" |
|
|
|
|
|
async def test_agent(): |
|
print("=== STARTING AGENT TEST ===") |
|
try: |
|
# Create agent |
|
print("Creating agent...") |
|
external_client = AsyncOpenAI( |
|
base_url = '${baseUrl.replace(/'/g, "\\'")}', |
|
api_key='${apiKey.replace(/'/g, "\\'")}', # required, but may be unused depending on server |
|
) |
|
set_default_openai_client(external_client) |
|
|
|
# Build tools list conditionally |
|
tools = [count_character_occurrences, visit_webpage] |
|
${tavilyApiKey ? 'tools.append(search_tavily)' : '# search_tavily tool not added (no API key)'} |
|
|
|
agent = Agent( |
|
name="Tool caller", |
|
instructions="You are a helpful agent. Use the available tools to answer the questions.", |
|
tools=tools, |
|
model=OpenAIChatCompletionsModel( |
|
model="${modelName.replace(/'/g, "\\'")}", |
|
openai_client=external_client, |
|
), |
|
model_settings=ModelSettings( |
|
extra_args={"timeout": 90} |
|
) |
|
) |
|
print(f"Agent created: {agent}") |
|
print(f"Using server: ${baseUrl} with model: ${modelName}") |
|
|
|
# Run the agent |
|
print("Running agent...") |
|
result = await Runner.run(agent, """${prompt.replace(/"/g, '\\"')}""", max_turns=20) |
|
print(f"Agent run completed!") |
|
print(f"Result type: {type(result)}") |
|
print(f"Result: {result}") |
|
|
|
# Try to access final_output |
|
if hasattr(result, 'final_output'): |
|
print(f"Final output: {result.final_output}") |
|
print(f"Final output type: {type(result.final_output)}") |
|
return str(result.final_output) |
|
else: |
|
print("No final_output attribute found") |
|
print(f"Available attributes: {dir(result)}") |
|
return "" |
|
print("=== AGENT TEST COMPLETED ===") |
|
|
|
except Exception as e: |
|
print(f"=== AGENT TEST FAILED ===") |
|
print(f"Error: {e}") |
|
import traceback |
|
print("Traceback:") |
|
print(traceback.format_exc()) |
|
|
|
# Run the test |
|
final_result = await test_agent() |
|
final_result |
|
`); |
|
|
|
// Display the result |
|
if (typeof result === 'string' && result.length > 0 && !result.startsWith('Error:')) { |
|
const formattedResult = result.replace(/\\n/g, '\n'); |
|
logAgent("π Agent code ran successfully! Check console for Python output", 'success'); |
|
logAgent("", 'info'); |
|
logAgent("π AGENT RESPONSE:", 'info'); |
|
logAgent("β".repeat(50), 'info'); |
|
logAgent("\n"+formattedResult, 'info'); |
|
logAgent("β".repeat(50), 'info'); |
|
} else { |
|
logAgent("β Agent execution failed or returned empty result", 'error'); |
|
logAgent(`Result: ${result}`, 'error'); |
|
} |
|
|
|
} catch (error) { |
|
logAgent(`Agent execution failed: ${error}`, 'error'); |
|
console.error('Full error:', error); |
|
} finally { |
|
document.getElementById('runBtn').disabled = false; |
|
hideRunning(); |
|
} |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
|