upgrade run ui

This commit is contained in:
Shivering Ocelot committed 2026-06-06 16:36:01 -05:00
1 parent bb204f2c6c
commit a10224d21e
6 files changed
+91 -18

No files matched your search

-1
View File
@@ -1,5 +1,4 @@
import re
import json
import inspect
import time
import asyncio
+3 -1
View File
@@ -18,8 +18,10 @@ frontend_path = '../../frontend/dist'
def encode_json(value):
def encode_unknown(value):
if value is ToolSpec:
if isinstance(value, ToolSpec):
return value.to_openai_dict()
elif isinstance(value, Stream):
return {'streamId': value.id}
else:
return repr(value)
return json.dumps(value, default=encode_unknown)
+27 -3
View File
@@ -115,6 +115,30 @@ class ToolSpec:
def __repr__(self):
return f"ToolSpec(name={self.name!r}, params={[p.name for p in self.parameters]})"
class Tool:
"""Base class for tools that implement their logic in a 'run' method.
Subclasses should override 'run' to provide the tool's functionality.
"""
def __init__(self):
pass
def run(self, **kwargs):
"""Execute the tool. To be overridden by subclasses."""
raise NotImplementedError("Tool subclasses must override the 'run' method.")
def to_spec(self):
"""Generate a ToolSpec from this tool instance."""
return ToolSpec(
name=None, # TODO: Computed from class name, or from class attribute
description=None, # TODO: From class doc comment
parameters=None, # TODO: Class attribute
func=self.run,
)
def __repr__(self):
return f"Tool(name={self.name!r}, params={[p.name for p in self.parameters]})"
class ToolCall:
"""Represents a single tool invocation request emitted by the model.
@@ -123,7 +147,7 @@ class ToolCall:
id: Unique call identifier (used by OpenAI / llama-cpp protocol).
tool_name: Name of the tool to invoke.
arguments: Dict of argument name → value.
result: String content returned by the tool.
result: String content returned by the tool.
is_error: Whether the result represents an error.
"""
@@ -136,10 +160,10 @@ class ToolCall:
def __repr__(self):
return f"ToolCall(tool={self.tool_name!r}, args={self.arguments!r})"
def call_message(self):
raise Exception("TODO: Llama message for tool call")
def result_message(self):
from inference import LlamaMessage
return LlamaMessage("tool", self.result)
+6 -4
View File
@@ -7,14 +7,11 @@ import ContextMenu from './components/main/ContextMenu.vue'
import * as ui from './ui';
import * as backend from './backend';
import * as runs from './runs';
// possible states: loading, login, signup, logged
const loginState = ref('loading');
function triggerAlert () {
ui.alert("This is an alert");
}
function handleAuthResult (success) {
loginState.value = success ? 'logged' : 'login';
}
@@ -42,6 +39,11 @@ function signupSubmit (formElem) {
backend.signup(username, email, password).then(handleAuthResult);
}
const runsListener = runs.onCreated(run => {
console.log("new run", run);
});
runsListener.allRuns = true; // hack?
backend.authResult().then(handleAuthResult);
</script>
+20 -4
View File
@@ -6,15 +6,18 @@ let unsubscribeRunEvents = null;
// Event listeners for UI components
const listeners = {
created: [],
runs: [],
chats: {},
};
class RunListener {
constructor() {
constructor() {
this.runIds = new Set();
this.callbacks = [];
this.cancelled = false;
this.cancelled = false;
this.allRuns = false;
}
setIdList(runIds) {
@@ -35,7 +38,7 @@ class RunListener {
}
hasRunId(runId) {
return this.runIds.has(runId);
return this.allRuns || this.runIds.has(runId);
}
emitChange(data) {
@@ -56,6 +59,9 @@ function updateRun(event) {
if (!run) {
run = Object.assign({}, data, {id: runId, events: []});
runs.push(run);
for (let cb of listeners.created) {
cb(run);
}
} else {
console.warn("Created but existing?", run, data);
}
@@ -71,7 +77,7 @@ function updateRun(event) {
}
if (run) {
run.events.push(event);
run.events.push({name, runId, data});
for (let listener of listeners.runs) {
if (listener.hasRunId(runId)) {
listener.emitChange(run);
@@ -118,6 +124,16 @@ export async function cancelRun(runId) {
}
// Subscribe to runs changes
export function onCreated(callback) {
listeners.created.push(callback);
return () => {
const index = listeners.created.indexOf(callback);
if (index > -1) {
listeners.created.splice(index, 1);
}
};
}
export function addRunListener(callback) {
const listener = new RunListener();
listener.addCallback(callback);
+35 -5
View File
@@ -2,10 +2,15 @@
import { ref, onMounted, onUnmounted } from 'vue';
import TitleBar from '../components/TitleBar.vue';
import * as runs from '../runs';
import * as backend from '../backend';
const runList = ref([]);
const expandedRunId = ref(null);
const runListener = runs.addRunListener(onRunEvent);
const expandEvents = ref(false);
let cancelCreateListener = null;
let runListener = runs.addRunListener(onRunEvent);
let streamCache = {};
const toggleRun = (id) => {
expandedRunId.value = expandedRunId.value == id ? null : id;
@@ -21,7 +26,7 @@
};
function onRunEvent (newRun) {
let run = runList.value.findOne(r => r.id == newRun.id);
let run = runList.value.find(r => r.id == newRun.id);
Object.assign(run, newRun);
}
@@ -30,10 +35,31 @@
run => Object.assign({}, run)
);
runListener.setIdList(runList.value.map(r => r.id));
cancelCreateListener = runs.onCreated(run => {
runList.value.push(Object.assign({}, run));
});
});
function isStreamEvent(event) {
return typeof event.data == "object" && event.data.streamId != null;
}
function getStreamRef(event) {
let streamId = event.data.streamId;
let refObject = streamCache[streamId];
if (refObject) return refObject;
refObject = ref("");
let stream = backend.readStream(streamId);
stream.on("data", chunk => refObject.value += chunk);
streamCache[streamId] = refObject;
return refObject;
}
onUnmounted(() => {
runListener.setIdList([]);
cancelCreateListener();
});
</script>
@@ -75,9 +101,13 @@
<div class="detail-key">Input</div>
<pre class="detail-pre">{{ JSON.stringify(run.input, null, 2) }}</pre>
</div>
<div v-if="run.events && run.events.length > 0" class="detail-row">
<div class="detail-key">Events ({{ run.events.length }})</div>
<pre class="detail-pre">{{ JSON.stringify(run.events, null, 2) }}</pre>
<div class="detail-row" v-for="event in run.events">
<div class="detail-key">event: {{ event.name }}</div>
<template v-if="isStreamEvent(event)">
<div class="detail-value">Stream</div>
<pre class="detail-pre">{{ getStreamRef(event) }}</pre>
</template>
<pre v-else class="detail-pre">{{ JSON.stringify(event.data, null, 2) }}</pre>
</div>
<div v-if="run.output !== null && run.output !== undefined" class="detail-row">
<div class="detail-key">Output</div>