diff --git a/backend/src/agent.py b/backend/src/agent.py index 0780c34..93fe6d2 100644 --- a/backend/src/agent.py +++ b/backend/src/agent.py @@ -36,15 +36,25 @@ class Agent: # should be in expert init? self.tools = [] + self.remotes = [] for resource in self.resources: - if resource.kind != 'tool': continue - if resource.value.get('type') == 'native' and 'name' in resource.value: - tool_cls = next((t for t in self.app.tool_classes if t.tool_name() == resource.value['name']), None) - if tool_cls: - tool = tool_cls() - spec = tool.get_spec().clone() - spec.name = resource.key - self.tools.append(spec) + if resource.kind == 'tool': + if resource.value.get('type') == 'native' and 'name' in resource.value: + tool_cls = next((t for t in self.app.tool_classes if t.tool_name() == resource.value['name']), None) + if tool_cls: + tool = tool_cls() + spec = tool.get_spec().clone() + spec.name = resource.key + self.tools.append(spec) + elif resource.kind == 'remote': + value = resource.value or {} + if isinstance(value, str): + value = {'prefix': value} + self.remotes.append({ + 'name': resource.key, + 'prefix': value.get('prefix'), + }) + def _set_basic_info(self, id, name, user_id): self.id = id @@ -205,16 +215,21 @@ class Agent: if r.kind == kind and (key is None or r.key == key)] def get_tools(self, run=None): - """Collect the tools available to this agent. - - Returns the agent-configured tools (derived from its resources) plus - any remote tools referenced by the run input. When a run is given, - remote names in ``run.input['remotes']`` are resolved and appended. - """ - tools = list(self.tools) + remotes = list(self.remotes) if run is not None and 'remotes' in run.input: - tools += self.app.getRemoteTools(self.user_id, run.input['remotes']) - return tools + remotes += list(run.input['remotes']) + + tools = list(self.tools) + self.app.getRemoteTools(self.user_id, remotes) + + # Dedupe by tool name (agent-configured vs run-input vs native collisions) + seen, result = set(), [] + for tool in tools: + if tool.name in seen: + continue + seen.add(tool.name) + result.append(tool) + + return result class DebugAgent(Agent): diff --git a/backend/src/app_mixins/agent.py b/backend/src/app_mixins/agent.py index 2f0867a..38df2fa 100644 --- a/backend/src/app_mixins/agent.py +++ b/backend/src/app_mixins/agent.py @@ -126,13 +126,21 @@ class AgentMixin: async def save_agent_resource(self, agent_id, kind, key, value): await db.AgentResource.update_or_create( + {'value': value}, agent_id=agent_id, kind=kind, key=key, - value=value, ) print(f"Saved agent resource: {agent_id} - {kind} - {key}") + async def delete_agent_resource(self, agent_id, kind, key): + await db.AgentResource.filter( + agent_id=agent_id, + kind=kind, + key=key, + ).delete() + print(f"Deleted agent resource: {agent_id} - {kind} - {key}") + # --- Control & Actions --- async def get_agent_control(self, agent_id): diff --git a/backend/src/inference.py b/backend/src/inference.py index 8fe5ffc..76e9b5a 100644 --- a/backend/src/inference.py +++ b/backend/src/inference.py @@ -36,9 +36,9 @@ def format_tools(tools): if isinstance(t, dict): specs.append(t) elif isinstance(t, ToolSpec): - specs.append(t.format_openai()) + specs.append(t.to_openai_dict()) elif isinstance(t, Tool): - specs.append(t.get_spec().format_openai()) + specs.append(t.get_spec().to_openai_dict()) return specs def format_openai(messages): diff --git a/backend/src/server/websocket.py b/backend/src/server/websocket.py index 35e2182..49851c1 100644 --- a/backend/src/server/websocket.py +++ b/backend/src/server/websocket.py @@ -150,6 +150,10 @@ def register(server, app, auth_manager): await app.save_agent_resource(await agent_id_from_body(), body['kind'], body['key'], body['value']) await send({'message': "resource saved", 'nonce': nonce}) + elif action == "delete-agent-resource": + await app.delete_agent_resource(await agent_id_from_body(), body['kind'], body['key']) + await send({'message': "resource deleted", 'nonce': nonce}) + elif action == "get-agent-runs": await send({ 'runs': await app.get_agent_runs(agent_id=await agent_id_from_body(allow_none=True), user_id=user_id), diff --git a/frontend/src/components/JsonEditor.vue b/frontend/src/components/JsonEditor.vue index dc0edb5..344c117 100644 --- a/frontend/src/components/JsonEditor.vue +++ b/frontend/src/components/JsonEditor.vue @@ -1,5 +1,6 @@ @@ -327,36 +359,31 @@ -
-

Tools

-
-
{{ resource.key }}
-
{{ JSON.stringify(resource.value, null, 2) }}
-
-
-
- -
- - -
-
-

Resources

-
-
{{ resource.kind }}:{{ resource.key }}
-
{{ JSON.stringify(resource.value, null, 2) }}
+
+
+
+ {{ targetResource.kind }} + {{ targetResource.key }} +
+ +
+
+
+ {{ resource.kind }} {{ resource.key }} +
+
+
+
+ +
-
-
-
- -
- - -
-
@@ -398,4 +425,37 @@ resize: vertical; height: 5em; } + + .resource-table { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(6em, 1fr)); + height: fit-content; + flex: 1; + + /*align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + border-bottom: 1px solid var(--color-border);*/ + } + .resource-entry { + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + background-color: var(--color-surface-strong); + padding: .4rem .6rem; + width: max-content; + } + .resource-entry:hover { + border-color: var(--color-highlight); + } + + .resource-type { + font-family: monospace; + border: 1px solid var(--color-border); + border-radius: 3px; + padding: .1rem .3rem; + } + + /* style fix */ + .row > section { margin-left: 0; } + .row > section:last-child { margin-right: 0; } \ No newline at end of file