Search
Code3,900
} else if (step.endpoint) { this.log("INFO", stepComponent, \`Calling endpoint: \${step.endpoint}\`); const isStreaming = (step.endpoint === "/api/tools/openai_call" || step.endpoint === "/api/tools/refinement") && resolvedInputs.stream === true; // --- CRITICAL: You MUST include EITHER 'action' OR 'endpoint' --- "action": "client:action_name", // Use for client tools (human_input, transform) "endpoint": "/api/tools/tool_name", // Use for backend tools (openai_call, http_fetch) // --- A step CANNOT have both or neither. ---Every single step in the \`steps\` array **MUST** have exactly one of the following properties:* \`"action"\`: Use this **only** for client-side tools listed in the manifest (e.g., \`"client:human_input"\`, \`"client:transform"\`).* \`"endpoint"\`: Use this **only** for backend tools listed in the manifest (e.g., \`"/api/tools/openai_call"\`, \`"/api/tools/http_fetch"\`).**A step will fail if it has NEITHER or BOTH.** The error you just saw (\`Step must have 'action' or 'endpoint'\`) was caused by forgetting this rule. "id": "process_data", "run_if": "this.check_for_error.apiFailed !== true", "endpoint": "/api/tools/openai_call", "inputs": { "messages": [...] }, "outputs": { "summary": "payload.choices[0].message.content" }This is how you create a draft, evaluate it, and then refine it.1. **'write_draft'**: (openai_call) -> outputs 'draft_text'2. **'evaluate_draft'**: (qa_evaluation) - inputs: { "briefing": "...", "first_draft": "{{write_draft.draft_text}}" } }, { tool: "openai_call", endpoint: "/api/tools/openai_call", description: "Calls an OpenAI model for analysis, generation, or extraction.", inputs: { messages: "array (e.g., [{role: 'user', content: '...'}])", response_format: "object (optional, e.g., {type: 'json_object'})", }, outputs: "payload (OpenAI completion object)", }, { stream: "boolean (optional, default false)", }, outputs: "payload (OpenAI completion object or stream)", }, // --- *** NEW YAHOO FINANCE TOOL DEFINITION *** ---import { Hono } from "npm:hono@4.4.12";import { OpenAI } from "https://esm.town/v/std/openai";import type { Context } from "npm:hono@4.4.12";import { streamText } from "npm:hono@4.4.12/streaming";};const openai = new OpenAI(); // Initialize once// --- NEW YAHOO FINANCE CLIENT ---const yahooFinance = new YahooFinance();}async function handleOpenAICall( payload: { messages: any[];): Promise<any> { const { messages, model = "gpt-4o-mini", stream, ...rest } = payload; log("INFO", `Calling OpenAI model: ${model}`, { stream: !!stream, ...rest }); try { return await openai.chat.completions.create({ model, messages, }); } catch (e) { log("ERROR", `OpenAI call failed: ${e.message}`, e.stack); throw e; } briefing, ); const criteriaCompletion = await handleOpenAICall({ model: config.models.minor, response_format: { type: "json_object" }, raw_output: first_draft, }; const evalCompletion = await handleOpenAICall({ model: config.models.minor, response_format: { type: "json_object" }, try { const result = await handleOpenAICall({ model: config.models.major, // Use best model for refinement messages: [{ role: "system", content: config.prompts.REFINER_AGENT }, {const toolRegistry = { "http_fetch": handleHttpFetch, "openai_call": handleOpenAICall, "text_manipulation": handleTextManipulation, "qa_evaluation": handleQaEvaluation, // NEW try { const response = await handleOpenAICall({ model: config.models.major, // Use your best model response_format: { type: "json_object" }, // Handle streaming for any tool that supports it const isStreaming = (toolName === "openai_call" || toolName === "refinement") && payload.stream === true;// @ts-nocheckimport { Hono } from "npm:hono@4.4.12";import { OpenAI } from "https://esm.town/v/std/openai";import type { Context } from "npm:hono@4.4.12";import { streamText } from "npm:hono@4.4.12/streaming";}async function handleOpenAICall( payload: { messages: any[]; const { messages, model = "gpt-4o-mini", stream, ...rest } = payload; try { const openai = new OpenAI(); if (!stream) { return await openai.chat.completions.create({ model, messages, }); } return await openai.chat.completions.create({ model, messages,const toolRegistry = { "http_fetch": handleHttpFetch, "openai_call": handleOpenAICall, "text_manipulation": handleTextManipulation,};function generateClientHtml() { return `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Workflow Engine</title><style>*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;display:flex;height:100vh;margin:0;background-color:#f4f7f6}.sidebar{width:450px;min-width:380px;background:#fff;border-right:1px solid #e0e0e0;display:flex;flex-direction:column;box-shadow:0 0 10px rgba(0,0,0,0.02)}.main-content{flex-grow:1;display:flex;flex-direction:column;overflow-y:auto}.config-area{padding:20px;border-bottom:1px solid #e0e0e0}.status-area{padding:20px;overflow-y:auto;flex-grow:1}.results-area{padding:20px;overflow-y:auto;flex-grow:1}h1,h2{margin-top:0;color:#333}h1{font-size:1.6rem}h2{font-size:1.2rem;border-bottom:1px solid #eee;padding-bottom:8px}label{display:block;margin:10px 0 5px;font-weight:600;font-size:0.9rem}textarea{width:100%;box-sizing:border-box;border:1px solid #ccc;border-radius:4px;padding:8px;font-family:"SFMono-Regular",Consolas,monospace;font-size:0.85rem;min-height:300px}button{background-color:#007bff;color:white;border:none;padding:10px 15px;border-radius:4px;cursor:pointer;font-size:1rem;margin-top:10px;width:100%}button:disabled{background-color:#ccc;cursor:not-allowed}pre{background-color:#2d2d2d;color:#f1f1f1;padding:15px;border-radius:4px;white-space:pre-wrap;word-wrap:break-word;font-size:0.85rem;max-height:500px;overflow-y:auto}.step-status{padding:8px;margin-bottom:5px;border-radius:4px;border-left:4px solid;font-size:0.9rem}.status-PENDING{border-color:#6c757d;background-color:#f8f9fa}.status-RUNNING{border-color:#007bff;background-color:#e3f2fd}.status-COMPLETED{border-color:#28a745;background-color:#d4edda}.status-FAILED{border-color:#dc3545;background-color:#f8d7da}.status-SKIPPED{border-color:#ffc107;background-color:#fff3cd}.log-box{max-height:400px;overflow-y:auto;background:#fdfdfd;border:1px solid #eee;border-radius:4px;padding:10px}.log-entry{font-family:"SFMono-Regular",Consolas,monospace;font-size:0.8rem;padding:2px 4px;border-bottom:1px solid #f0f0f0}.log-INFO{color:#333}.log-SUCCESS{color:#28a745;font-weight:bold}.log-WARN{color:#fd7e14}.log-ERROR{color:#dc3545;font-weight:bold}#streamingOutputBox{border:1px dashed #007bff;padding:10px;border-radius:4px;margin-bottom:15px}#streamingOutputBox h3{margin-top:0;font-size:1rem;color:#007bff}.example-btn{background-color:#6c757d;font-size:0.85rem;padding:6px 10px;margin:5px 5px 5px 0;width:auto}.example-buttons{display:flex;flex-wrap:wrap;margin-top:10px}</style></head><body><div class="sidebar"><div class="config-area"><h1>Workflow Engine</h1><label for="workflowDefinition">Workflow Definition (JSON):</label><textarea id="workflowDefinition"></textarea><div class="example-buttons"><button class="example-btn" id="exampleGitHub">GitHub Triage</button><button class="example-btn" id="exampleAIContent">AI Content</button><button class="example-btn" id="exampleResearch">Research</button><button class="example-btn" id="exampleWeather">Weather</button><button class="example-btn" id="exampleQuote">Quote</button><button class="example-btn" id="exampleConditional">Conditional</button></div><button id="runWorkflowBtn">βΆ Run Workflow</button></div><div class="status-area"><h2>Status</h2><div id="overallStatus" class="step-status status-PENDING">Idle</div><div id="stepStatusBox"></div></div></div><div class="main-content"><div class="results-area"><h2>Results</h2><div id="streamingOutputBox" style="display:none;"></div><h3>Final Outputs</h3><pre id="resultsBox">Results will appear here...</pre><h3>Logs</h3><div class="log-box"><pre id="logBox">Logs will appear here...</pre></div></div></div><script>(function(){const workflows={githubTriage:{"id":"github_issue_triage","description":"Fetches and triages open issues from a GitHub repo","steps":[{"id":"get_repo","description":"Ask user for GitHub repo","action":"client:human_input","inputs":{"prompt":"Enter GitHub repo (e.g., honojs/hono):"},"outputs":{"repoName":"payload"}},{"id":"fetch_issues","description":"Fetch top 5 open issues","endpoint":"/api/tools/http_fetch","inputs":{"url":"https://api.github.com/repos/{{get_repo.repoName}}/issues?state=open&per_page=5"},"outputs":{"issues":"payload"}},{"id":"check_for_error","description":"Check if API fetch failed","action":"client:transform","inputs":{"data":"{{fetch_issues.issues}}","script":"Array.isArray(data) ? false : (data?.message?.includes('Not Found') || false)"},"outputs":{"apiFailed":"payload"}},{"id":"handle_api_fail","description":"Report API failure","action":"client:transform","run_if":"this.check_for_error.apiFailed === true","inputs":{"data":null,"script":"'Failed to fetch repo. Check spelling or repo visibility.'"},"outputs":{"summary":"payload"}},{"id":"format_issues","description":"Format issues for AI","action":"client:transform","run_if":"this.check_for_error.apiFailed !== true && Array.isArray(this.fetch_issues.issues)","inputs":{"data":"{{fetch_issues.issues}}","script":"data.length > 0 ? data.map(i => ({ number: i.number, title: i.title, user: i.user.login, labels: i.labels.map(l => l.name) })) : []"},"outputs":{"formattedIssues":"payload"}},{"id":"triage_issues","description":"AI triage of issues","endpoint":"/api/tools/openai_call","run_if":"Array.isArray(this.format_issues.formattedIssues) && this.format_issues.formattedIssues.length > 0","inputs":{"messages":[{"role":"system","content":"You are a senior GitHub triage engineer. Briefly summarize the open issues provided. Look for patterns, common themes, or priority issues. Keep it concise and actionable."},{"role":"user","content":"Analyze these {{format_issues.formattedIssues.length}} open issues:\\n\\n{{format_issues.formattedIssues}}"}],"model":"gpt-4o-mini","stream":true},"outputs":{"summary":"payload.choices[0].message.content"}},{"id":"no_issues_message","description":"Message when no issues found","action":"client:transform","run_if":"!this.format_issues.formattedIssues || this.format_issues.formattedIssues.length === 0","inputs":{"data":null,"script":"'No open issues found in this repository.'"},"outputs":{"summary":"payload"}}],"finalOutputs":{"repo":"this.get_repo.repoName","issue_count":"Array.isArray(this.format_issues.formattedIssues) ? this.format_issues.formattedIssues.length : 0","triage_summary":"this.triage_issues.summary || this.no_issues_message.summary || this.handle_api_fail.summary || 'No summary available'"}},aiContentChain:{"id":"ai_content_chain","description":"Uses AI to brainstorm, then write, then count words","steps":[{"id":"get_topic","description":"Ask user for blog post topic","action":"client:human_input","inputs":{"prompt":"What topic for a blog post?"},"outputs":{"topic":"payload"}},{"id":"brainstorm_outline","description":"Brainstorm outline (JSON Mode)","endpoint":"/api/tools/openai_call","inputs":{"messages":[{"role":"system","content":"You are a content strategist. Generate a 5-point blog post outline for the user's topic. Respond ONLY with a valid JSON object matching the requested format."},{"role":"user","content":"Topic: {{get_topic.topic}}. Format: { \\"outline\\": [\\"point1\\", \\"point2\\", \\"point3\\", \\"point4\\", \\"point5\\"] }"}],"model":"gpt-4o-mini","response_format":{"type":"json_object"}},"outputs":{"outlineJson":"payload.choices[0].message.content"}},{"id":"parse_outline","description":"Parse JSON outline safely","action":"client:transform","inputs":{"data":"{{brainstorm_outline.outlineJson}}","script":"typeof data === 'string' ? JSON.parse(data) : data"},"outputs":{"outlineParsed":"payload"}},{"id":"write_post","description":"Write blog post (Streaming)","endpoint":"/api/tools/openai_call","inputs":{"messages":[{"role":"system","content":"You are a helpful blog post writer. You write in a clear, engaging, and concise style. Write a blog post based on the provided outline."},{"role":"user","content":"Topic: {{get_topic.topic}}\\n\\nWrite a blog post following this outline: {{parse_outline.outlineParsed}}"}],"model":"gpt-4o-mini","stream":true},"outputs":{"fullPost":"payload.choices[0].message.content"}},{"id":"get_word_count","description":"Count words in post","endpoint":"/api/tools/text_manipulation","inputs":{"operation":"wordCount","text":"{{write_post.fullPost}}"},"outputs":{"wordCount":"payload.result"}}],"finalOutputs":{"topic":"this.get_topic.topic","outline":"this.parse_outline.outlineParsed","word_count":"this.get_word_count.wordCount","full_post":"this.write_post.fullPost"}},researchAssistant:{"id":"research_assistant","description":"Multi-source research with synthesis","steps":[{"id":"get_research_topic","description":"Ask user for research topic","action":"client:human_input","inputs":{"prompt":"What topic would you like to research?"},"outputs":{"topic":"payload"}},{"id":"fetch_wikipedia","description":"Fetch from Wikipedia API","endpoint":"/api/tools/http_fetch","inputs":{"url":"https://en.wikipedia.org/api/rest_v1/page/summary/{{get_research_topic.topic}}"},"outputs":{"wikiData":"payload","wikiExtract":"payload.extract"}},{"id":"check_wiki_error","description":"Check if Wikipedia failed","action":"client:transform","inputs":{"data":"{{fetch_wikipedia.wikiData}}","script":"data?.type === 'https://mediawiki.org/wiki/HyperSwitch/errors/not_found'"},"outputs":{"wikiFailed":"payload"}},{"id":"fetch_random_fact","description":"Get a random interesting fact","endpoint":"/api/tools/http_fetch","inputs":{"url":"https://uselessfacts.jsph.pl/random.json?language=en"},"outputs":{"randomFact":"payload.text"}},{"id":"count_wiki_words","description":"Count Wikipedia extract words","endpoint":"/api/tools/text_manipulation","run_if":"this.check_wiki_error.wikiFailed !== true && this.fetch_wikipedia.wikiExtract","inputs":{"operation":"wordCount","text":"{{fetch_wikipedia.wikiExtract}}"},"outputs":{"wikiWordCount":"payload.result"}},{"id":"synthesize_research","description":"AI synthesis of research","endpoint":"/api/tools/openai_call","run_if":"this.check_wiki_error.wikiFailed !== true","inputs":{"messages":[{"role":"system","content":"You are a research assistant. Create a brief, informative summary combining the Wikipedia info and the random fact. Make creative connections between them if possible."},{"role":"user","content":"Topic: {{get_research_topic.topic}}\\n\\nWikipedia: {{fetch_wikipedia.wikiExtract}}\\n\\nInteresting fact: {{fetch_random_fact.randomFact}}\\n\\nCreate a synthesized research summary."}],"model":"gpt-4o-mini","stream":true},"outputs":{"synthesis":"payload.choices[0].message.content"}},{"id":"wiki_not_found_message","description":"Handle Wikipedia not found","action":"client:transform","run_if":"this.check_wiki_error.wikiFailed === true","inputs":{"data":"{{get_research_topic.topic}}","script":"\`Wikipedia article not found for '\${data}'. Try a different topic or check spelling.\`"},"outputs":{"errorMessage":"payload"}},{"id":"save_research","description":"Save to localStorage","action":"client:storage_set","run_if":"this.synthesize_research.synthesis","inputs":{"key":"last_research_{{get_research_topic.topic}}","value":{"topic":"{{get_research_topic.topic}}","synthesis":"{{synthesize_research.synthesis}}","timestamp":"{{Date.now()}}"}}}],"finalOutputs":{"topic":"this.get_research_topic.topic","wikipedia_found":"this.check_wiki_error.wikiFailed !== true","word_count":"this.count_wiki_words.wikiWordCount || 0","random_fact":"this.fetch_random_fact.randomFact","synthesis":"this.synthesize_research.synthesis || this.wiki_not_found_message.errorMessage"}},weather:{"id":"weather_analysis","description":"Fetches weather data and analyzes it with AI","steps":[{"id":"get_city","description":"Ask user for a city name","action":"client:human_input","inputs":{"prompt":"Enter a city name (e.g., London, Tokyo, New York):"},"outputs":{"cityName":"payload"}},{"id":"fetch_weather","description":"Fetch current weather data","endpoint":"/api/tools/http_fetch","inputs":{"url":"https://wttr.in/{{get_city.cityName}}?format=j1"},"outputs":{"weatherData":"payload","temp":"payload.current_condition[0].temp_C","description":"payload.current_condition[0].weatherDesc[0].value"}},{"id":"format_temp","description":"Format temperature string","action":"client:transform","inputs":{"data":{"temp":"{{fetch_weather.temp}}","city":"{{get_city.cityName}}","desc":"{{fetch_weather.description}}"},"script":"\`Current weather in \${data.city}: \${data.temp}Β°C, \${data.desc}\`"},"outputs":{"formattedWeather":"payload"}},{"id":"analyze_weather","description":"Get AI analysis of the weather","endpoint":"/api/tools/openai_call","inputs":{"messages":[{"role":"system","content":"You are a helpful weather assistant. Provide a brief, friendly analysis of the weather and suggest one activity."},{"role":"user","content":"{{format_temp.formattedWeather}}"}],"model":"gpt-4o-mini","stream":false},"outputs":{"analysis":"payload.choices[0].message.content"}}],"finalOutputs":{"city":"this.get_city.cityName","temperature":"this.fetch_weather.temp","conditions":"this.fetch_weather.description","ai_analysis":"this.analyze_weather.analysis"}},quote:{"id":"quote_generator","description":"Gets a random quote and analyzes it","steps":[{"id":"fetch_quote","description":"Fetch random quote","endpoint":"/api/tools/http_fetch","inputs":{"url":"https://api.quotable.io/random"},"outputs":{"quoteText":"payload.content","author":"payload.author"}},{"id":"count_words","description":"Count words in quote","endpoint":"/api/tools/text_manipulation","inputs":{"operation":"wordCount","text":"{{fetch_quote.quoteText}}"},"outputs":{"wordCount":"payload.result"}},{"id":"uppercase_author","description":"Uppercase author name","endpoint":"/api/tools/text_manipulation","inputs":{"operation":"toUpperCase","text":"{{fetch_quote.author}}"},"outputs":{"authorUpper":"payload.result"}},{"id":"analyze_quote_streaming","description":"Analyze the quote with streaming","endpoint":"/api/tools/openai_call","inputs":{"messages":[{"role":"system","content":"You are a philosophical assistant. Analyze quotes thoughtfully."},{"role":"user","content":"Analyze this quote by {{uppercase_author.authorUpper}}: '{{fetch_quote.quoteText}}'"}],"model":"gpt-4o-mini","stream":true},"outputs":{"analysis":"payload.choices[0].message.content"}}],"finalOutputs":{"quote":"this.fetch_quote.quoteText","author":"this.fetch_quote.author","word_count":"this.count_words.wordCount","ai_analysis":"this.analyze_quote_streaming.analysis"}},conditional:{"id":"conditional_workflow","description":"Demonstrates conditional execution","steps":[{"id":"get_number","description":"Ask user for a number","action":"client:human_input","inputs":{"prompt":"Enter a number (try 5 or 15):"},"outputs":{"userNumber":"payload"}},{"id":"parse_number","description":"Parse number from string","action":"client:transform","inputs":{"data":"{{get_number.userNumber}}","script":"parseInt(data, 10)"},"outputs":{"numValue":"payload"}},{"id":"check_if_large","description":"Check if number is > 10","action":"client:transform","inputs":{"data":"{{parse_number.numValue}}","script":"data > 10"},"outputs":{"isLarge":"payload"}},{"id":"fetch_large_number_fact","description":"Fetch fact (only if large)","endpoint":"/api/tools/http_fetch","run_if":"this.check_if_large.isLarge === true","inputs":{"url":"http://numbersapi.com/{{parse_number.numValue}}/math"},"outputs":{"fact":"payload"}},{"id":"small_number_message","description":"Message for small numbers","action":"client:transform","run_if":"this.check_if_large.isLarge === false","inputs":{"data":"{{parse_number.numValue}}","script":"\`\${data} is a small number (β€ 10). Try a bigger one!\`"},"outputs":{"message":"payload"}},{"id":"save_to_storage","description":"Save last number to storage","action":"client:storage_set","inputs":{"key":"last_number","value":"{{parse_number.numValue}}"}}],"finalOutputs":{"number":"this.parse_number.numValue","is_large":"this.check_if_large.isLarge","large_fact":"this.fetch_large_number_fact.fact || 'N/A'","small_message":"this.small_number_message.message || 'N/A'"}}};const $=id=>document.getElementById(id);const runWorkflowBtn=$('runWorkflowBtn'),workflowDefTextarea=$('workflowDefinition'),logBox=$('logBox'),stepStatusBox=$('stepStatusBox'),resultsBox=$('resultsBox'),overallStatusEl=$('overallStatus'),streamingOutputBox=$('streamingOutputBox');workflowDefTextarea.value=JSON.stringify(workflows.githubTriage,null,2);$('exampleGitHub').addEventListener('click',()=>{workflowDefTextarea.value=JSON.stringify(workflows.githubTriage,null,2)});$('exampleAIContent').addEventListener('click',()=>{workflowDefTextarea.value=JSON.stringify(workflows.aiContentChain,null,2)});$('exampleResearch').addEventListener('click',()=>{workflowDefTextarea.value=JSON.stringify(workflows.researchAssistant,null,2)});$('exampleWeather').addEventListener('click',()=>{workflowDefTextarea.value=JSON.stringify(workflows.weather,null,2)});$('exampleQuote').addEventListener('click',()=>{workflowDefTextarea.value=JSON.stringify(workflows.quote,null,2)});$('exampleConditional').addEventListener('click',()=>{workflowDefTextarea.value=JSON.stringify(workflows.conditional,null,2)});function clearLogs(){logBox.textContent='';resultsBox.textContent='Results will appear here...';stepStatusBox.innerHTML='';streamingOutputBox.innerHTML='';streamingOutputBox.style.display='none'}function appendLog(log){const entry=document.createElement('div');entry.className=\`log-entry log-\${log.level}\`;const time=new Date(log.timestamp).toLocaleTimeString();entry.textContent=\`[\${time}] [\${log.component}] \${log.message}\`;if(log.details){const detailsPre=document.createElement('pre');detailsPre.textContent=JSON.stringify(log.details,null,2);entry.appendChild(detailsPre)}logBox.appendChild(entry);logBox.parentElement.scrollTop=logBox.parentElement.scrollHeight}function clientLog(level,component,message,details){appendLog({timestamp:new Date().toISOString(),level,component,message,details})}function updateStepStatusUI(stepId,status,message=''){let statusDiv=$(\`status-\${stepId}\`);if(!statusDiv){statusDiv=document.createElement('div');statusDiv.id=\`status-\${stepId}\`;stepStatusBox.appendChild(statusDiv)}statusDiv.className=\`step-status status-\${status.toUpperCase()}\`;statusDiv.innerHTML=\`<strong>\${stepId}</strong>: \${message||status}\`}function updateOverallStatus(statusText,statusType="PENDING"){overallStatusEl.className=\`step-status status-\${statusType.toUpperCase()}\`;overallStatusEl.textContent=statusText}function resolveValue(path,context){try{const segments=path.match(/([^.\\[\\]]+|\\[\\d+\\])/g)||[];let current=context;for(const segment of segments){if(current===null||current===undefined)return undefined;if(segment.startsWith('[')&&segment.endsWith(']')){const index=parseInt(segment.slice(1,-1),10);current=Array.isArray(current)?current[index]:undefined}else{current=(typeof current==='object')?current[segment]:undefined}}return current}catch(e){return undefined}}function resolveInputStructure(structure,context){if(structure===null||structure===undefined)return structure;if(typeof structure==='string'){const purePlaceholder=structure.match(/^\\{\\{\\s*([^}]+)\\s*\\}\\}$/);if(purePlaceholder){const resolved=resolveValue(purePlaceholder[1].trim(),context);return resolved}return structure.replace(/\\{\\{\\s*([^}]+)\\s*\\}\\}/g,(match,path)=>{const resolved=resolveValue(path.trim(),context);if(resolved===undefined)return'[undefined]';if(resolved===null)return'[null]';if(typeof resolved==='object')return JSON.stringify(resolved);return String(resolved)})}if(Array.isArray(structure))return structure.map(item=>resolveInputStructure(item,context));if(typeof structure==='object'){const resolvedObject={};for(const key in structure){resolvedObject[key]=resolveInputStructure(structure[key],context)}return resolvedObject}return structure}const SHADOWED_GLOBALS="window,document,localStorage,sessionStorage,fetch,XMLHttpRequest,alert,prompt,confirm",SHADOWED_GLOBALS_ARRAY=SHADOWED_GLOBALS.split(','),UNDEFINED_ARGS=Array(SHADOWED_GLOBALS_ARRAY.length).fill(undefined);const clientActions={"human_input":async(inputs,log)=>{const message=inputs.prompt||"Agent requires input:";const userInput=window.prompt(message);if(userInput===null)throw new Error("User cancelled input.");return userInput},"transform":async(inputs,log)=>{const{script,data}=inputs;if(!script)throw new Error("Missing 'script' parameter");try{let cleanScript=script.trim();if(cleanScript.startsWith('return '))cleanScript=cleanScript.substring(7);if(cleanScript.endsWith(';'))cleanScript=cleanScript.slice(0,-1).trim();const sandboxedFunc=new Function("data",\`return (\${cleanScript})\`);const result=sandboxedFunc.call(null,data);return result}catch(e){throw new Error(\`Transform failed: \${e.message}\`)}},"storage_set":async(inputs,log)=>{const{key,value}=inputs;if(!key)throw new Error("Missing 'key' parameter");try{const stringValue=JSON.stringify(value);localStorage.setItem(key,stringValue);return{success:true,key}}catch(e){throw e}},"storage_get":async(inputs,log)=>{const{key}=inputs;if(!key)throw new Error("Missing 'key' parameter");try{const value=localStorage.getItem(key);if(value===null)return null;const parsedValue=JSON.parse(value);return parsedValue}catch(e){throw e}},"storage_remove":async(inputs,log)=>{const{key}=inputs;if(!key)throw new Error("Missing 'key' parameter");try{localStorage.removeItem(key);return{success:true,key}}catch(e){throw e}}};runWorkflowBtn.addEventListener('click',async()=>{runWorkflowBtn.disabled=true;clearLogs();updateOverallStatus('Initializing...','RUNNING');let workflowDef;try{workflowDef=JSON.parse(workflowDefTextarea.value)}catch(e){clientLog("ERROR","Orchestrator","Invalid Workflow JSON",e.message);updateOverallStatus('Error: Invalid Workflow JSON','FAILED');runWorkflowBtn.disabled=false;return}if(!workflowDef.steps||!Array.isArray(workflowDef.steps)){clientLog("ERROR","Orchestrator","Workflow must have 'steps' array");updateOverallStatus('Error: Invalid workflow structure','FAILED');runWorkflowBtn.disabled=false;return}for(let i=0;i<workflowDef.steps.length;i++){const step=workflowDef.steps[i];if(!step.id){clientLog("ERROR","Orchestrator",\`Step at index \${i} missing 'id' field\`);updateOverallStatus(\`Error: Step \${i} missing 'id'\`,'FAILED');runWorkflowBtn.disabled=false;return}if(!step.action&&!step.endpoint){clientLog("ERROR","Orchestrator",\`Step '\${step.id}' must have either 'action' or 'endpoint'\`);updateOverallStatus(\`Error: Step '\${step.id}' invalid\`,'FAILED');runWorkflowBtn.disabled=false;return}}const stepIds=workflowDef.steps.map(s=>s.id),duplicates=stepIds.filter((id,index)=>stepIds.indexOf(id)!==index);if(duplicates.length>0){clientLog("ERROR","Orchestrator",\`Duplicate step IDs found: \${duplicates.join(', ')}\`);updateOverallStatus(\`Error: Duplicate step IDs\`,'FAILED');runWorkflowBtn.disabled=false;return}const workflowInstanceId=\`wfi-\${Date.now()}\`,stepOutputs={},completedSteps=new Set();clientLog("INFO","Orchestrator",\`Starting workflow: \${workflowDef.id}\`,{totalSteps:workflowDef.steps.length});updateOverallStatus(\`Running: \${workflowDef.id}\`,'RUNNING');workflowDef.steps.forEach(step=>{updateStepStatusUI(step.id,"PENDING")});for(const step of workflowDef.steps){const stepComponent=\`Step:\${step.id}\`;updateStepStatusUI(step.id,"RUNNING","Evaluating...");clientLog("INFO",stepComponent,\`Processing step: \${step.description}\`);try{if(step.dependencies&&step.dependencies.length>0){const unmetDeps=step.dependencies.filter(depId=>!completedSteps.has(depId));if(unmetDeps.length>0){const errorMsg=\`Unmet dependencies: \${unmetDeps.join(', ')}\`;clientLog("WARN",stepComponent,errorMsg);updateStepStatusUI(step.id,"SKIPPED",errorMsg);stepOutputs[step.id]={_skipped:true,_error:errorMsg};continue}const failedDeps=step.dependencies.filter(depId=>stepOutputs[depId]&&stepOutputs[depId]._error&&!stepOutputs[depId]._skipped);if(failedDeps.length>0){const errorMsg=\`Failed dependencies: \${failedDeps.join(', ')}\`;clientLog("WARN",stepComponent,errorMsg);updateStepStatusUI(step.id,"SKIPPED",errorMsg);stepOutputs[step.id]={_skipped:true,_error:errorMsg};continue}}if(step.run_if){let shouldRun=false;try{const evalFunc=new Function(...SHADOWED_GLOBALS_ARRAY,\`try{with(this){return Boolean(\${step.run_if})}}catch(e){console.error('run_if evaluation error:',e);return false}\`);shouldRun=evalFunc.call(stepOutputs,...UNDEFINED_ARGS);clientLog("INFO",stepComponent,\`Condition evaluated: \${shouldRun}\`,{condition:step.run_if})}catch(e){clientLog("WARN",stepComponent,"Failed to evaluate run_if condition",{condition:step.run_if,error:e.message});shouldRun=false}if(!shouldRun){const skipMsg="Condition not met (run_if)";clientLog("INFO",stepComponent,skipMsg);updateStepStatusUI(step.id,"SKIPPED",skipMsg);stepOutputs[step.id]={_skipped:true};completedSteps.add(step.id);continue}}const resolvedInputs=resolveInputStructure(step.inputs||{},stepOutputs);clientLog("INFO",stepComponent,"Inputs resolved",{hasInputs:Object.keys(resolvedInputs).length>0});updateStepStatusUI(step.id,"RUNNING",\`Executing: \${step.description}\`);let rawResultPayload;if(step.action&&step.action.startsWith("client:")){const actionName=step.action.split(":")[1];if(!clientActions[actionName])throw new Error(\`Unknown client action: \${actionName}\`);clientLog("INFO",stepComponent,\`Executing client action: \${actionName}\`);rawResultPayload=await clientActions[actionName](resolvedInputs,(lvl,msg,details)=>clientLog(lvl,\`Action:\${step.id}\`,msg,details))}else if(step.endpoint){clientLog("INFO",stepComponent,\`Calling endpoint: \${step.endpoint}\`);const apiRequest={workflowInstanceId,stepId:step.id,payload:resolvedInputs};const isStreaming=step.endpoint==="/api/tools/openai_call"&&resolvedInputs.stream===true;const response=await fetch(step.endpoint,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(apiRequest)});if(!response.ok){const errorText=await response.text();throw new Error(\`API Error \${response.status}: \${errorText.substring(0,200)}\`)}if(isStreaming){clientLog("INFO",stepComponent,"Receiving streaming response...");streamingOutputBox.style.display='block';streamingOutputBox.innerHTML=\`<h3>π΄ Streaming: \${step.id}</h3><pre id="stream-\${step.id}" style="max-height: 300px; overflow-y: auto;"></pre>\`;const streamPre=$(\`stream-\${step.id}\`),reader=response.body.getReader(),decoder=new TextDecoder();let fullStreamedResponse="";while(true){const{done,value}=await reader.read();if(done)break;const chunk=decoder.decode(value,{stream:true});fullStreamedResponse+=chunk;streamPre.textContent+=chunk;streamPre.scrollTop=streamPre.scrollHeight}rawResultPayload={choices:[{message:{content:fullStreamedResponse}}]};clientLog("SUCCESS",stepComponent,"Stream complete",{length:fullStreamedResponse.length})}else{const apiResponse=await response.json();if(apiResponse.logs&&Array.isArray(apiResponse.logs)){apiResponse.logs.forEach(log=>appendLog(log))}if(apiResponse.error)throw new Error(apiResponse.error);rawResultPayload=apiResponse.payload}}else{throw new Error("Step must have either 'action' or 'endpoint'")}stepOutputs[step.id]={};if(step.outputs){const outputContext={payload:rawResultPayload};for(const outKey in step.outputs){const path=step.outputs[outKey];const value=resolveInputStructure(\`{{\${path}}}\`,outputContext);stepOutputs[step.id][outKey]=value}clientLog("INFO",stepComponent,"Outputs mapped",{outputKeys:Object.keys(step.outputs)})}else{stepOutputs[step.id]={payload:rawResultPayload}}if(!stepOutputs[step.id].payload){stepOutputs[step.id].payload=rawResultPayload}completedSteps.add(step.id);clientLog("SUCCESS",stepComponent,"Step completed successfully");updateStepStatusUI(step.id,"COMPLETED","Success")}catch(error){const errorMessage=error?.message||String(error)||'Unknown error',errorStack=error?.stack||'';clientLog("ERROR",stepComponent,"Step failed",{error:errorMessage,stack:errorStack.substring(0,200)});updateStepStatusUI(step.id,"FAILED",errorMessage.substring(0,60));stepOutputs[step.id]={_error:errorMessage,_errorStack:errorStack};if(!step.continueOnError){clientLog("ERROR","Orchestrator","Stopping workflow due to critical failure.",{stepId:step.id,error:errorMessage});updateOverallStatus(\`Error: Step '\${step.id}' failed - \${errorMessage.substring(0,40)}...\`,'FAILED');try{const finalResults={},finalOutputsDef=workflowDef.finalOutputs||{};for(const key in finalOutputsDef){const expression=finalOutputsDef[key];try{const evalFunc=new Function(...SHADOWED_GLOBALS_ARRAY,\`try{with(this){return \${expression}}}catch(e){return undefined}\`);finalResults[key]=evalFunc.call(stepOutputs,...UNDEFINED_ARGS)}catch(e){finalResults[key]=undefined}}resultsBox.textContent=JSON.stringify({_workflow_stopped:true,_failed_at_step:step.id,_error:errorMessage,...finalResults},null,2)}catch(e){resultsBox.textContent=JSON.stringify({_workflow_stopped:true,_failed_at_step:step.id,_error:errorMessage},null,2)}runWorkflowBtn.disabled=false;return}clientLog("WARN","Orchestrator","Step failed, but 'continueOnError' is true. Continuing...",{stepId:step.id})}}clientLog("INFO","Orchestrator","Computing final outputs...");try{const finalResults={},finalOutputsDef=workflowDef.finalOutputs||{};for(const key in finalOutputsDef){const expression=finalOutputsDef[key];try{const evalFunc=new Function(...SHADOWED_GLOBALS_ARRAY,\`try{with(this){return \${expression}}}catch(e){return 'ERROR: '+e.message}\`);finalResults[key]=evalFunc.call(stepOutputs,...UNDEFINED_ARGS)}catch(e){clientLog("WARN","Orchestrator",\`Failed to evaluate output: \${key}\`,{expression,error:e.message});finalResults[key]=\`EVALUATION_ERROR: \${e.message}\`}}resultsBox.textContent=JSON.stringify(finalResults,null,2);clientLog("SUCCESS","Orchestrator","Final outputs computed",{outputCount:Object.keys(finalResults).length})}catch(e){clientLog("ERROR","Orchestrator","Failed to compute final outputs",e.message);resultsBox.textContent=JSON.stringify({error:"Failed to compute final outputs",message:e.message},null,2)}const totalSteps=workflowDef.steps.length,failedSteps=workflowDef.steps.filter(s=>stepOutputs[s.id]?._error&&!stepOutputs[s.id]?._skipped).length,skippedSteps=workflowDef.steps.filter(s=>stepOutputs[s.id]?._skipped).length,successSteps=completedSteps.size;let finalStatus,finalStatusType;if(failedSteps>0){finalStatus=\`Completed with \${failedSteps} error(s)\`;finalStatusType='FAILED'}else if(successSteps===0&&skippedSteps===totalSteps){finalStatus=\`Completed: 0 steps executed.\`;finalStatusType='COMPLETED'}else{finalStatus=\`Completed successfully (\${successSteps}/\${totalSteps} executed)\`;finalStatusType='COMPLETED'}updateOverallStatus(finalStatus,finalStatusType);clientLog("SUCCESS","Orchestrator","Workflow execution finished",{total:totalSteps,completed:successSteps,failed:failedSteps,skipped:skippedSteps});runWorkflowBtn.disabled=false});updateOverallStatus('Ready to run','PENDING');clientLog("INFO","System","Workflow engine initialized")})();</script></body></html>`;} }; try { if (toolName === "openai_call" && payload.stream === true) { log("INFO", "Starting streaming response"); const stream = await handleOpenAICall(payload, log); return streamText(c, async (s) => { try {import { Hono } from "npm:hono@4.4.12";import { OpenAI } from "https://esm.town/v/std/openai";import type { Context } from "npm:hono@4.4.12";import { streamText } from "npm:hono@4.4.12/streaming";/** * OpenAI Call Tool (Streaming and Non-Streaming) */async function handleOpenAICall( payload: { messages: any[];): Promise<any> { const { messages, model = "gpt-4o-mini", stream, ...rest } = payload; log("INFO", `Making OpenAI call to ${model}`, { stream, messages, ...rest }); try { const openai = new OpenAI(); if (!stream) { const completion = await openai.chat.completions.create({ model, messages, ...rest, }); log("SUCCESS", "OpenAI non-streaming call successful."); return completion; } log("INFO", "Preparing for streaming call..."); return await openai.chat.completions.create({ model, messages, }); } catch (e) { log("ERROR", "OpenAI API call failed.", e.message); throw e; }const toolRegistry = { "http_fetch": handleHttpFetch, "openai_call": handleOpenAICall, "text_manipulation": handleTextManipulation,}; "id": "triage_issues", "description": "AI triage of issues", "endpoint": "/api/tools/openai_call", "run_if": "typeof this.format_issues.formattedIssues === 'object'", "inputs": { "id": "brainstorm_outline", "description": "Brainstorm outline (JSON Mode)", "endpoint": "/api/tools/openai_call", "inputs": { "messages": [ "id": "write_post", "description": "Write blog post (Streaming)", "endpoint": "/api/tools/openai_call", "inputs": { "messages": [ "id": "analyze_weather", "description": "Get AI analysis of the weather", "endpoint": "/api/tools/openai_call", "inputs": { "messages": [ "id": "analyze_quote_streaming", "description": "Analyze the quote with streaming", "endpoint": "/api/tools/openai_call", "inputs": { "messages": [ }; const isStreaming = step.endpoint === "/api/tools/openai_call" && resolvedInputs.stream === true; try { // SPECIAL CASE: STREAMING OPENAI CALL if (toolName === "openai_call" && payload.stream === true) { log("INFO", "Starting streaming response"); const stream = await handleOpenAICall(payload, log); return streamText(c, async (s) => {Note: When changing a SQLite table's schema, change the table's name (e.g., add _2 or _3) to create a fresh table.### OpenAI```tsimport { OpenAI } from "https://esm.town/v/std/openai";const openai = new OpenAI();const completion = await openai.chat.completions.create({ messages: [ { role: "user", content: "Say hello in a creative way" },import { OpenAI } from "https://esm.town/v/std/openai";export default async function(req: Request) { try { const openai = new OpenAI(); const formData = await req.formData(); const file = formData.get("file") as File; const blob = new Blob([arrayBuffer], { type }); const audioFile = new File([blob], file.name); const transcription = await openai.audio.transcriptions.create({ file: audioFile, model: "whisper-1", } // πΉ Add your OpenAI moderation logic here (this is the code you asked about) const response = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [ // πΉ Error handling for empty responses if (!response?.choices?.[0]?.message?.content) { throw new Error("Empty response from OpenAI"); }import React, { useEffect, useState } from "https://esm.sh/react@18.2.0";import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";import { OpenAI } from "https://esm.town/v/std/openai";// ======== BACKEND HANDLER ======== if (req.method === "POST") { try { const openai = new OpenAI(); const formData = await req.formData(); const file = formData.get("file"); const blob = new Blob([arrayBuffer], { type: type }); const audioFile = new File([blob], file.name); const transcription = await openai.audio.transcriptions.create({ file: audioFile, model: "whisper-1", // πΉ AI Moderation Check const response = await openai.chat.completions.create({ model: "gpt-4o", messages: [import { blob } from "https://esm.town/v/std/blob";import { readFile } from "https://esm.town/v/std/utils@85-main/index.ts";import { OpenAI } from "https://esm.town/v/std/openai";import { Todo } from "../shared/types.ts";const TODOS_BLOB_KEY = "todo_app_items_v1";const openai = new OpenAI();const app = new Hono(); : "none yet"; const completion = await openai.chat.completions.create({ model: "gpt-4o-mini", temperature: 0.7,Note: When changing a SQLite table's schema, change the table's name (e.g., add _2 or _3) to create a fresh table.### OpenAI```tsimport { OpenAI } from "https://esm.town/v/std/openai";const openai = new OpenAI();const completion = await openai.chat.completions.create({ messages: [ { role: "user", content: "Say hello in a creative way" },reconsumeralization
import { OpenAI } from "https://esm.town/v/std/openai";
import { sqlite } from "https://esm.town/v/stevekrouse/sqlite";
/**
* Practical Implementation of Collective Content Intelligence
* Bridging advanced AI with collaborative content creation
*/
exp
kwhinnery_openai
lost1991
import { OpenAI } from "https://esm.town/v/std/openai";
export default async function(req: Request): Promise<Response> {
if (req.method === "OPTIONS") {
return new Response(null, {
headers: {
"Access-Control-Allow-Origin": "*",
No docs found