diff --git a/backend/src/agents/__init__.py b/backend/src/agents/__init__.py index 8e1aea1..dc17f72 100644 --- a/backend/src/agents/__init__.py +++ b/backend/src/agents/__init__.py @@ -1,2 +1,3 @@ from .character import CharacterAgent -from .expert import ExpertAgent \ No newline at end of file +from .expert import ExpertAgent +from .scripted import ScriptedAgent \ No newline at end of file diff --git a/backend/src/agents/scripted.py b/backend/src/agents/scripted.py new file mode 100644 index 0000000..bdfd6b0 --- /dev/null +++ b/backend/src/agents/scripted.py @@ -0,0 +1,31 @@ +import re +import json +import inspect +from string import Template + +from inference import LlamaMessage, replicate_llama_completion, Models +from agent import Agent, AgentField + +CLASS_DESCRIPTION = """ +Scripted agents can be configured with custom Python actions. +The 'actions' config field has a list of action names. Each action is configured in a corresponding "action_name" with the name of the action, and contains: argument (object schema object), and body (python string source). +""".strip() + +class ScriptedAgent(Agent): + identifier = "scripted" + description = CLASS_DESCRIPTION + schema_fields = [ + AgentField('actions', 'array', default=[]), + ] + + def __init__(self, app, data): + Agent.__init__(self, app, data) + + async def prompt(self, instruction, input=None, **kwargs): + raise Exception("Scripted agent cannot use prompts (Unimplemented)") + + async def received_message(self, message): + raise Exception("Scripted agent cannot use chat (Unimplemented)") + + async def execute_action(self, name: str, argument: dict): + raise Exception("No actions for this agent") \ No newline at end of file diff --git a/backend/src/app.py b/backend/src/app.py index f0a30f7..33617ac 100644 --- a/backend/src/app.py +++ b/backend/src/app.py @@ -39,6 +39,7 @@ class App(ChatMixin): agent_mod.BasicAgent, agents_mod.CharacterAgent, agents_mod.ExpertAgent, + agents_mod.ScriptedAgent, #agent_mod.LlamaAgent, #agent_mod.CoderV1Agent, diff --git a/frontend/src/views/Agent.vue b/frontend/src/views/Agent.vue index 440c07e..9a43071 100644 --- a/frontend/src/views/Agent.vue +++ b/frontend/src/views/Agent.vue @@ -19,6 +19,8 @@ const agent = ref({}); const kindInfo = ref({}); const configFields = ref([]); + // Only used for scripted agents. + const actionDefinitions = ref([]); // === State Management === // @@ -40,9 +42,14 @@ backend.sendAction("get-agent-kind-info", {kind: _agent.kind}).then(res => { kindInfo.value.description = res.info.description; + kindInfo.value.name = _agent.kind; }); backend.sendAction('get-agent-config-fields', {agentId}).then(res => { + const dataObject = Object.fromEntries(res.fields.map( + field => [field.name, field.value] + )); + configFields.value = res.fields.map(field => ({ name: field.name, type: field.type, @@ -50,6 +57,18 @@ default: null, enabled: field.value != null, })); + + if (dataObject.actions) { + actionDefinitions.value = dataObject.actions.map(name => { + const fieldName = 'action_' + name; + const fieldData = dataObject[fieldName]; + return { + name, + argument: fieldData ? JSON.stringify(fieldData.argument) : "{}", + body: fieldData && fieldData.body || "pass", + } + }); + } }); } @@ -186,6 +205,59 @@ backend.invalidateCache({agents: true}); } } + + // == Scripted Agent Actions == // + function createAction () { + const value = prompt(`Enter a new identifier for ${agent.value.name}`); + if (value) { + const def = { + name: value, + argument: "{}", + body: "pass", + }; + const actionsField = configFields.value.find(field => field.name == 'actions'); + if (actionsField) { + actionsField.value.push(def.name); + } else { + configFields.value.push({ + name: def.name, + value: [def.name], + enabled: true, + }); + } + saveAction(def); + } else { + ui.alert('Action creation cancelled'); + } + } + // Update the config fields relevant to the action and saves changes automatically. Doesn't update 'actions'. + function saveAction (actionDef) { + let argSchema; + try { + argSchema = JSON.parse(actionDef.argument); + } catch (e) { + ui.alert("Bad Argument JSON: " + e.message); + return; + } + + const fieldName = 'action_' + actionDef.name; + const actionField = configFields.value.find(field => field.name == fieldName); + if (actionField) { + actionField.arguments = argSchema; + actionField.body = actionDef.body; + } else { + configFields.value.push({ + name: fieldName, + value: { + argument: argSchema, + body: actionDef.body, + }, + enabled: true, + }); + } + + saveChanges(); + }