mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
feat: subagent table structure and crud apis
This commit is contained in:
+4
-1
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/memohai/memoh/internal/schedule"
|
||||
"github.com/memohai/memoh/internal/settings"
|
||||
"github.com/memohai/memoh/internal/server"
|
||||
"github.com/memohai/memoh/internal/subagent"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -168,7 +169,9 @@ func main() {
|
||||
log.Fatalf("schedule bootstrap: %v", err)
|
||||
}
|
||||
scheduleHandler := handlers.NewScheduleHandler(scheduleService)
|
||||
srv := server.NewServer(addr, cfg.Auth.JWTSecret, pingHandler, authHandler, memoryHandler, embeddingsHandler, chatHandler, swaggerHandler, providersHandler, modelsHandler, settingsHandler, historyHandler, scheduleHandler, containerdHandler)
|
||||
subagentService := subagent.NewService(queries)
|
||||
subagentHandler := handlers.NewSubagentHandler(subagentService)
|
||||
srv := server.NewServer(addr, cfg.Auth.JWTSecret, pingHandler, authHandler, memoryHandler, embeddingsHandler, chatHandler, swaggerHandler, providersHandler, modelsHandler, settingsHandler, historyHandler, scheduleHandler, subagentHandler, containerdHandler)
|
||||
|
||||
if err := srv.Start(); err != nil {
|
||||
log.Fatalf("server failed: %v", err)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
DROP TABLE IF EXISTS user_settings;
|
||||
DROP TABLE IF EXISTS history;
|
||||
DROP TABLE IF EXISTS schedule;
|
||||
DROP TABLE IF EXISTS subagents;
|
||||
DROP TABLE IF EXISTS lifecycle_events;
|
||||
DROP TABLE IF EXISTS container_versions;
|
||||
DROP TABLE IF EXISTS models;
|
||||
|
||||
@@ -163,3 +163,21 @@ CREATE TABLE IF NOT EXISTS schedule (
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_schedule_user_id ON schedule(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_schedule_enabled ON schedule(enabled);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS subagents (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
messages JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
skills JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
CONSTRAINT subagents_name_unique UNIQUE (name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_subagents_user_id ON subagents(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_subagents_deleted ON subagents(deleted);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
-- name: CreateSubagent :one
|
||||
INSERT INTO subagents (name, description, user_id, messages, metadata, skills)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills;
|
||||
|
||||
-- name: GetSubagentByID :one
|
||||
SELECT id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
FROM subagents
|
||||
WHERE id = $1 AND deleted = false;
|
||||
|
||||
-- name: ListSubagentsByUser :many
|
||||
SELECT id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
FROM subagents
|
||||
WHERE user_id = $1 AND deleted = false
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: UpdateSubagent :one
|
||||
UPDATE subagents
|
||||
SET name = $2,
|
||||
description = $3,
|
||||
metadata = $4,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills;
|
||||
|
||||
-- name: UpdateSubagentMessages :one
|
||||
UPDATE subagents
|
||||
SET messages = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills;
|
||||
|
||||
-- name: UpdateSubagentSkills :one
|
||||
UPDATE subagents
|
||||
SET skills = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills;
|
||||
|
||||
-- name: SoftDeleteSubagent :exec
|
||||
UPDATE subagents
|
||||
SET deleted = true,
|
||||
deleted_at = now(),
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false;
|
||||
|
||||
+599
@@ -1921,6 +1921,448 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents": {
|
||||
"get": {
|
||||
"description": "List subagents for current user",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "List subagents",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.ListResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a subagent for current user",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Create subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Subagent payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.CreateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents/{id}": {
|
||||
"get": {
|
||||
"description": "Get a subagent by ID",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Get subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update a subagent by ID",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Update subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Subagent payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.UpdateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a subagent by ID",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Delete subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents/{id}/context": {
|
||||
"get": {
|
||||
"description": "Get a subagent's message context",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Get subagent context",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.ContextResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update a subagent's message context",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Update subagent context",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Context payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.UpdateContextRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.ContextResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents/{id}/skills": {
|
||||
"get": {
|
||||
"description": "Get a subagent's skills",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Get subagent skills",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.SkillsResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Replace a subagent's skills",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Update subagent skills",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Skills payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.UpdateSkillsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.SkillsResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Add skills to a subagent",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Add subagent skills",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Skills payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.AddSkillsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.SkillsResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -2775,6 +3217,163 @@ const docTemplate = `{
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.AddSkillsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.ContextResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.CreateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.ListResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.SkillsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.Subagent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"deleted_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.UpdateContextRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.UpdateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.UpdateSkillsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
@@ -1912,6 +1912,448 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents": {
|
||||
"get": {
|
||||
"description": "List subagents for current user",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "List subagents",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.ListResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Create a subagent for current user",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Create subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Subagent payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.CreateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "Created",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents/{id}": {
|
||||
"get": {
|
||||
"description": "Get a subagent by ID",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Get subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update a subagent by ID",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Update subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Subagent payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.UpdateRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"description": "Delete a subagent by ID",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Delete subagent",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents/{id}/context": {
|
||||
"get": {
|
||||
"description": "Get a subagent's message context",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Get subagent context",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.ContextResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Update a subagent's message context",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Update subagent context",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Context payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.UpdateContextRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.ContextResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/subagents/{id}/skills": {
|
||||
"get": {
|
||||
"description": "Get a subagent's skills",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Get subagent skills",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.SkillsResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"description": "Replace a subagent's skills",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Update subagent skills",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Skills payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.UpdateSkillsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.SkillsResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"description": "Add skills to a subagent",
|
||||
"tags": [
|
||||
"subagent"
|
||||
],
|
||||
"summary": "Add subagent skills",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Subagent ID",
|
||||
"name": "id",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"description": "Skills payload",
|
||||
"name": "payload",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.AddSkillsRequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/subagent.SkillsResponse"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Not Found",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ErrorResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
@@ -2766,6 +3208,163 @@
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.AddSkillsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.ContextResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.CreateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.ListResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/subagent.Subagent"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.SkillsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.Subagent": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"deleted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"deleted_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"user_id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.UpdateContextRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"messages": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.UpdateRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"subagent.UpdateSkillsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skills": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -563,6 +563,110 @@ definitions:
|
||||
max_context_load_time:
|
||||
type: integer
|
||||
type: object
|
||||
subagent.AddSkillsRequest:
|
||||
properties:
|
||||
skills:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
subagent.ContextResponse:
|
||||
properties:
|
||||
messages:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
subagent.CreateRequest:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
messages:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
metadata:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
skills:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
subagent.ListResponse:
|
||||
properties:
|
||||
items:
|
||||
items:
|
||||
$ref: '#/definitions/subagent.Subagent'
|
||||
type: array
|
||||
type: object
|
||||
subagent.SkillsResponse:
|
||||
properties:
|
||||
skills:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
subagent.Subagent:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
deleted:
|
||||
type: boolean
|
||||
deleted_at:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
messages:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
metadata:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
skills:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
updated_at:
|
||||
type: string
|
||||
user_id:
|
||||
type: string
|
||||
type: object
|
||||
subagent.UpdateContextRequest:
|
||||
properties:
|
||||
messages:
|
||||
items:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
subagent.UpdateRequest:
|
||||
properties:
|
||||
description:
|
||||
type: string
|
||||
metadata:
|
||||
additionalProperties: true
|
||||
type: object
|
||||
name:
|
||||
type: string
|
||||
type: object
|
||||
subagent.UpdateSkillsRequest:
|
||||
properties:
|
||||
skills:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
info:
|
||||
contact: {}
|
||||
title: Memoh API
|
||||
@@ -1828,4 +1932,297 @@ paths:
|
||||
summary: Update user settings
|
||||
tags:
|
||||
- settings
|
||||
/subagents:
|
||||
get:
|
||||
description: List subagents for current user
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.ListResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: List subagents
|
||||
tags:
|
||||
- subagent
|
||||
post:
|
||||
description: Create a subagent for current user
|
||||
parameters:
|
||||
- description: Subagent payload
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.CreateRequest'
|
||||
responses:
|
||||
"201":
|
||||
description: Created
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.Subagent'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Create subagent
|
||||
tags:
|
||||
- subagent
|
||||
/subagents/{id}:
|
||||
delete:
|
||||
description: Delete a subagent by ID
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"204":
|
||||
description: No Content
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Delete subagent
|
||||
tags:
|
||||
- subagent
|
||||
get:
|
||||
description: Get a subagent by ID
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.Subagent'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Get subagent
|
||||
tags:
|
||||
- subagent
|
||||
put:
|
||||
description: Update a subagent by ID
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Subagent payload
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.UpdateRequest'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.Subagent'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Update subagent
|
||||
tags:
|
||||
- subagent
|
||||
/subagents/{id}/context:
|
||||
get:
|
||||
description: Get a subagent's message context
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.ContextResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Get subagent context
|
||||
tags:
|
||||
- subagent
|
||||
put:
|
||||
description: Update a subagent's message context
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Context payload
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.UpdateContextRequest'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.ContextResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Update subagent context
|
||||
tags:
|
||||
- subagent
|
||||
/subagents/{id}/skills:
|
||||
get:
|
||||
description: Get a subagent's skills
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.SkillsResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Get subagent skills
|
||||
tags:
|
||||
- subagent
|
||||
post:
|
||||
description: Add skills to a subagent
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Skills payload
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.AddSkillsRequest'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.SkillsResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Add subagent skills
|
||||
tags:
|
||||
- subagent
|
||||
put:
|
||||
description: Replace a subagent's skills
|
||||
parameters:
|
||||
- description: Subagent ID
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: Skills payload
|
||||
in: body
|
||||
name: payload
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.UpdateSkillsRequest'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/subagent.SkillsResponse'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"404":
|
||||
description: Not Found
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ErrorResponse'
|
||||
summary: Update subagent skills
|
||||
tags:
|
||||
- subagent
|
||||
swagger: "2.0"
|
||||
|
||||
@@ -105,6 +105,20 @@ type Snapshot struct {
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
}
|
||||
|
||||
type Subagent struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
CreatedAt pgtype.Timestamptz `json:"created_at"`
|
||||
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
|
||||
Deleted bool `json:"deleted"`
|
||||
DeletedAt pgtype.Timestamptz `json:"deleted_at"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
Messages []byte `json:"messages"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
Skills []byte `json:"skills"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Username string `json:"username"`
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.30.0
|
||||
// source: subagents.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const createSubagent = `-- name: CreateSubagent :one
|
||||
INSERT INTO subagents (name, description, user_id, messages, metadata, skills)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
`
|
||||
|
||||
type CreateSubagentParams struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
UserID pgtype.UUID `json:"user_id"`
|
||||
Messages []byte `json:"messages"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
Skills []byte `json:"skills"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateSubagent(ctx context.Context, arg CreateSubagentParams) (Subagent, error) {
|
||||
row := q.db.QueryRow(ctx, createSubagent,
|
||||
arg.Name,
|
||||
arg.Description,
|
||||
arg.UserID,
|
||||
arg.Messages,
|
||||
arg.Metadata,
|
||||
arg.Skills,
|
||||
)
|
||||
var i Subagent
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Deleted,
|
||||
&i.DeletedAt,
|
||||
&i.UserID,
|
||||
&i.Messages,
|
||||
&i.Metadata,
|
||||
&i.Skills,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getSubagentByID = `-- name: GetSubagentByID :one
|
||||
SELECT id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
FROM subagents
|
||||
WHERE id = $1 AND deleted = false
|
||||
`
|
||||
|
||||
func (q *Queries) GetSubagentByID(ctx context.Context, id pgtype.UUID) (Subagent, error) {
|
||||
row := q.db.QueryRow(ctx, getSubagentByID, id)
|
||||
var i Subagent
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Deleted,
|
||||
&i.DeletedAt,
|
||||
&i.UserID,
|
||||
&i.Messages,
|
||||
&i.Metadata,
|
||||
&i.Skills,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listSubagentsByUser = `-- name: ListSubagentsByUser :many
|
||||
SELECT id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
FROM subagents
|
||||
WHERE user_id = $1 AND deleted = false
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
|
||||
func (q *Queries) ListSubagentsByUser(ctx context.Context, userID pgtype.UUID) ([]Subagent, error) {
|
||||
rows, err := q.db.Query(ctx, listSubagentsByUser, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Subagent
|
||||
for rows.Next() {
|
||||
var i Subagent
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Deleted,
|
||||
&i.DeletedAt,
|
||||
&i.UserID,
|
||||
&i.Messages,
|
||||
&i.Metadata,
|
||||
&i.Skills,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const softDeleteSubagent = `-- name: SoftDeleteSubagent :exec
|
||||
UPDATE subagents
|
||||
SET deleted = true,
|
||||
deleted_at = now(),
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
`
|
||||
|
||||
func (q *Queries) SoftDeleteSubagent(ctx context.Context, id pgtype.UUID) error {
|
||||
_, err := q.db.Exec(ctx, softDeleteSubagent, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateSubagent = `-- name: UpdateSubagent :one
|
||||
UPDATE subagents
|
||||
SET name = $2,
|
||||
description = $3,
|
||||
metadata = $4,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
`
|
||||
|
||||
type UpdateSubagentParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Metadata []byte `json:"metadata"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateSubagent(ctx context.Context, arg UpdateSubagentParams) (Subagent, error) {
|
||||
row := q.db.QueryRow(ctx, updateSubagent,
|
||||
arg.ID,
|
||||
arg.Name,
|
||||
arg.Description,
|
||||
arg.Metadata,
|
||||
)
|
||||
var i Subagent
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Deleted,
|
||||
&i.DeletedAt,
|
||||
&i.UserID,
|
||||
&i.Messages,
|
||||
&i.Metadata,
|
||||
&i.Skills,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateSubagentMessages = `-- name: UpdateSubagentMessages :one
|
||||
UPDATE subagents
|
||||
SET messages = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
`
|
||||
|
||||
type UpdateSubagentMessagesParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Messages []byte `json:"messages"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateSubagentMessages(ctx context.Context, arg UpdateSubagentMessagesParams) (Subagent, error) {
|
||||
row := q.db.QueryRow(ctx, updateSubagentMessages, arg.ID, arg.Messages)
|
||||
var i Subagent
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Deleted,
|
||||
&i.DeletedAt,
|
||||
&i.UserID,
|
||||
&i.Messages,
|
||||
&i.Metadata,
|
||||
&i.Skills,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateSubagentSkills = `-- name: UpdateSubagentSkills :one
|
||||
UPDATE subagents
|
||||
SET skills = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND deleted = false
|
||||
RETURNING id, name, description, created_at, updated_at, deleted, deleted_at, user_id, messages, metadata, skills
|
||||
`
|
||||
|
||||
type UpdateSubagentSkillsParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Skills []byte `json:"skills"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateSubagentSkills(ctx context.Context, arg UpdateSubagentSkillsParams) (Subagent, error) {
|
||||
row := q.db.QueryRow(ctx, updateSubagentSkills, arg.ID, arg.Skills)
|
||||
var i Subagent
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Name,
|
||||
&i.Description,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Deleted,
|
||||
&i.DeletedAt,
|
||||
&i.UserID,
|
||||
&i.Messages,
|
||||
&i.Metadata,
|
||||
&i.Skills,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/memohai/memoh/internal/auth"
|
||||
"github.com/memohai/memoh/internal/identity"
|
||||
"github.com/memohai/memoh/internal/subagent"
|
||||
)
|
||||
|
||||
type SubagentHandler struct {
|
||||
service *subagent.Service
|
||||
}
|
||||
|
||||
func NewSubagentHandler(service *subagent.Service) *SubagentHandler {
|
||||
return &SubagentHandler{service: service}
|
||||
}
|
||||
|
||||
func (h *SubagentHandler) Register(e *echo.Echo) {
|
||||
group := e.Group("/subagents")
|
||||
group.POST("", h.Create)
|
||||
group.GET("", h.List)
|
||||
group.GET("/:id", h.Get)
|
||||
group.PUT("/:id", h.Update)
|
||||
group.DELETE("/:id", h.Delete)
|
||||
group.GET("/:id/context", h.GetContext)
|
||||
group.PUT("/:id/context", h.UpdateContext)
|
||||
group.GET("/:id/skills", h.GetSkills)
|
||||
group.PUT("/:id/skills", h.UpdateSkills)
|
||||
group.POST("/:id/skills", h.AddSkills)
|
||||
}
|
||||
|
||||
// Create godoc
|
||||
// @Summary Create subagent
|
||||
// @Description Create a subagent for current user
|
||||
// @Tags subagent
|
||||
// @Param payload body subagent.CreateRequest true "Subagent payload"
|
||||
// @Success 201 {object} subagent.Subagent
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents [post]
|
||||
func (h *SubagentHandler) Create(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var req subagent.CreateRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
resp, err := h.service.Create(c.Request().Context(), userID, req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.JSON(http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
// List godoc
|
||||
// @Summary List subagents
|
||||
// @Description List subagents for current user
|
||||
// @Tags subagent
|
||||
// @Success 200 {object} subagent.ListResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents [get]
|
||||
func (h *SubagentHandler) List(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items, err := h.service.List(c.Request().Context(), userID)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.JSON(http.StatusOK, subagent.ListResponse{Items: items})
|
||||
}
|
||||
|
||||
// Get godoc
|
||||
// @Summary Get subagent
|
||||
// @Description Get a subagent by ID
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Success 200 {object} subagent.Subagent
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id} [get]
|
||||
func (h *SubagentHandler) Get(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
return c.JSON(http.StatusOK, item)
|
||||
}
|
||||
|
||||
// Update godoc
|
||||
// @Summary Update subagent
|
||||
// @Description Update a subagent by ID
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Param payload body subagent.UpdateRequest true "Subagent payload"
|
||||
// @Success 200 {object} subagent.Subagent
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id} [put]
|
||||
func (h *SubagentHandler) Update(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
var req subagent.UpdateRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
resp, err := h.service.Update(c.Request().Context(), id, req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// Delete godoc
|
||||
// @Summary Delete subagent
|
||||
// @Description Delete a subagent by ID
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Success 204 "No Content"
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id} [delete]
|
||||
func (h *SubagentHandler) Delete(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
if err := h.service.Delete(c.Request().Context(), id); err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.NoContent(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// GetContext godoc
|
||||
// @Summary Get subagent context
|
||||
// @Description Get a subagent's message context
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Success 200 {object} subagent.ContextResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id}/context [get]
|
||||
func (h *SubagentHandler) GetContext(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
return c.JSON(http.StatusOK, subagent.ContextResponse{Messages: item.Messages})
|
||||
}
|
||||
|
||||
// UpdateContext godoc
|
||||
// @Summary Update subagent context
|
||||
// @Description Update a subagent's message context
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Param payload body subagent.UpdateContextRequest true "Context payload"
|
||||
// @Success 200 {object} subagent.ContextResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id}/context [put]
|
||||
func (h *SubagentHandler) UpdateContext(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
var req subagent.UpdateContextRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
updated, err := h.service.UpdateContext(c.Request().Context(), id, req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.JSON(http.StatusOK, subagent.ContextResponse{Messages: updated.Messages})
|
||||
}
|
||||
|
||||
// GetSkills godoc
|
||||
// @Summary Get subagent skills
|
||||
// @Description Get a subagent's skills
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Success 200 {object} subagent.SkillsResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id}/skills [get]
|
||||
func (h *SubagentHandler) GetSkills(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
return c.JSON(http.StatusOK, subagent.SkillsResponse{Skills: item.Skills})
|
||||
}
|
||||
|
||||
// UpdateSkills godoc
|
||||
// @Summary Update subagent skills
|
||||
// @Description Replace a subagent's skills
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Param payload body subagent.UpdateSkillsRequest true "Skills payload"
|
||||
// @Success 200 {object} subagent.SkillsResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id}/skills [put]
|
||||
func (h *SubagentHandler) UpdateSkills(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
var req subagent.UpdateSkillsRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
updated, err := h.service.UpdateSkills(c.Request().Context(), id, req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.JSON(http.StatusOK, subagent.SkillsResponse{Skills: updated.Skills})
|
||||
}
|
||||
|
||||
// AddSkills godoc
|
||||
// @Summary Add subagent skills
|
||||
// @Description Add skills to a subagent
|
||||
// @Tags subagent
|
||||
// @Param id path string true "Subagent ID"
|
||||
// @Param payload body subagent.AddSkillsRequest true "Skills payload"
|
||||
// @Success 200 {object} subagent.SkillsResponse
|
||||
// @Failure 400 {object} ErrorResponse
|
||||
// @Failure 404 {object} ErrorResponse
|
||||
// @Failure 500 {object} ErrorResponse
|
||||
// @Router /subagents/{id}/skills [post]
|
||||
func (h *SubagentHandler) AddSkills(c echo.Context) error {
|
||||
userID, err := h.requireUserID(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id := c.Param("id")
|
||||
if id == "" {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "id is required")
|
||||
}
|
||||
var req subagent.AddSkillsRequest
|
||||
if err := c.Bind(&req); err != nil {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
item, err := h.service.Get(c.Request().Context(), id)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, err.Error())
|
||||
}
|
||||
if item.UserID != userID {
|
||||
return echo.NewHTTPError(http.StatusForbidden, "user mismatch")
|
||||
}
|
||||
updated, err := h.service.AddSkills(c.Request().Context(), id, req)
|
||||
if err != nil {
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
return c.JSON(http.StatusOK, subagent.SkillsResponse{Skills: updated.Skills})
|
||||
}
|
||||
|
||||
func (h *SubagentHandler) requireUserID(c echo.Context) (string, error) {
|
||||
userID, err := auth.UserIDFromContext(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := identity.ValidateUserID(userID); err != nil {
|
||||
return "", echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type Server struct {
|
||||
addr string
|
||||
}
|
||||
|
||||
func NewServer(addr string, jwtSecret string, pingHandler *handlers.PingHandler, authHandler *handlers.AuthHandler, memoryHandler *handlers.MemoryHandler, embeddingsHandler *handlers.EmbeddingsHandler, chatHandler *handlers.ChatHandler, swaggerHandler *handlers.SwaggerHandler, providersHandler *handlers.ProvidersHandler, modelsHandler *handlers.ModelsHandler, settingsHandler *handlers.SettingsHandler, historyHandler *handlers.HistoryHandler, scheduleHandler *handlers.ScheduleHandler, containerdHandler *handlers.ContainerdHandler) *Server {
|
||||
func NewServer(addr string, jwtSecret string, pingHandler *handlers.PingHandler, authHandler *handlers.AuthHandler, memoryHandler *handlers.MemoryHandler, embeddingsHandler *handlers.EmbeddingsHandler, chatHandler *handlers.ChatHandler, swaggerHandler *handlers.SwaggerHandler, providersHandler *handlers.ProvidersHandler, modelsHandler *handlers.ModelsHandler, settingsHandler *handlers.SettingsHandler, historyHandler *handlers.HistoryHandler, scheduleHandler *handlers.ScheduleHandler, subagentHandler *handlers.SubagentHandler, containerdHandler *handlers.ContainerdHandler) *Server {
|
||||
if addr == "" {
|
||||
addr = ":8080"
|
||||
}
|
||||
@@ -65,6 +65,9 @@ func NewServer(addr string, jwtSecret string, pingHandler *handlers.PingHandler,
|
||||
if scheduleHandler != nil {
|
||||
scheduleHandler.Register(e)
|
||||
}
|
||||
if subagentHandler != nil {
|
||||
subagentHandler.Register(e)
|
||||
}
|
||||
if providersHandler != nil {
|
||||
providersHandler.Register(e)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
package subagent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
||||
"github.com/memohai/memoh/internal/db/sqlc"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
queries *sqlc.Queries
|
||||
}
|
||||
|
||||
func NewService(queries *sqlc.Queries) *Service {
|
||||
return &Service{queries: queries}
|
||||
}
|
||||
|
||||
func (s *Service) Create(ctx context.Context, userID string, req CreateRequest) (Subagent, error) {
|
||||
if s.queries == nil {
|
||||
return Subagent{}, fmt.Errorf("subagent queries not configured")
|
||||
}
|
||||
name := strings.TrimSpace(req.Name)
|
||||
if name == "" {
|
||||
return Subagent{}, fmt.Errorf("name is required")
|
||||
}
|
||||
description := strings.TrimSpace(req.Description)
|
||||
if description == "" {
|
||||
return Subagent{}, fmt.Errorf("description is required")
|
||||
}
|
||||
pgUserID, err := parseUUID(userID)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
messagesPayload, err := marshalMessages(req.Messages)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
metadataPayload, err := marshalMetadata(req.Metadata)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
skillsPayload, err := marshalSkills(req.Skills)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
row, err := s.queries.CreateSubagent(ctx, sqlc.CreateSubagentParams{
|
||||
Name: name,
|
||||
Description: description,
|
||||
UserID: pgUserID,
|
||||
Messages: messagesPayload,
|
||||
Metadata: metadataPayload,
|
||||
Skills: skillsPayload,
|
||||
})
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
return toSubagent(row)
|
||||
}
|
||||
|
||||
func (s *Service) Get(ctx context.Context, id string) (Subagent, error) {
|
||||
pgID, err := parseUUID(id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
row, err := s.queries.GetSubagentByID(ctx, pgID)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return Subagent{}, fmt.Errorf("subagent not found")
|
||||
}
|
||||
return Subagent{}, err
|
||||
}
|
||||
return toSubagent(row)
|
||||
}
|
||||
|
||||
func (s *Service) List(ctx context.Context, userID string) ([]Subagent, error) {
|
||||
pgUserID, err := parseUUID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := s.queries.ListSubagentsByUser(ctx, pgUserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items := make([]Subagent, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
item, err := toSubagent(row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (s *Service) Update(ctx context.Context, id string, req UpdateRequest) (Subagent, error) {
|
||||
existing, err := s.Get(ctx, id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
name := existing.Name
|
||||
if req.Name != nil {
|
||||
name = strings.TrimSpace(*req.Name)
|
||||
if name == "" {
|
||||
return Subagent{}, fmt.Errorf("name is required")
|
||||
}
|
||||
}
|
||||
description := existing.Description
|
||||
if req.Description != nil {
|
||||
description = strings.TrimSpace(*req.Description)
|
||||
if description == "" {
|
||||
return Subagent{}, fmt.Errorf("description is required")
|
||||
}
|
||||
}
|
||||
metadata := existing.Metadata
|
||||
if req.Metadata != nil {
|
||||
metadata = req.Metadata
|
||||
}
|
||||
metadataPayload, err := marshalMetadata(metadata)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
pgID, err := parseUUID(id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
row, err := s.queries.UpdateSubagent(ctx, sqlc.UpdateSubagentParams{
|
||||
ID: pgID,
|
||||
Name: name,
|
||||
Description: description,
|
||||
Metadata: metadataPayload,
|
||||
})
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
return toSubagent(row)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateContext(ctx context.Context, id string, req UpdateContextRequest) (Subagent, error) {
|
||||
messagesPayload, err := marshalMessages(req.Messages)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
pgID, err := parseUUID(id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
row, err := s.queries.UpdateSubagentMessages(ctx, sqlc.UpdateSubagentMessagesParams{
|
||||
ID: pgID,
|
||||
Messages: messagesPayload,
|
||||
})
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
return toSubagent(row)
|
||||
}
|
||||
|
||||
func (s *Service) UpdateSkills(ctx context.Context, id string, req UpdateSkillsRequest) (Subagent, error) {
|
||||
skillsPayload, err := marshalSkills(req.Skills)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
pgID, err := parseUUID(id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
row, err := s.queries.UpdateSubagentSkills(ctx, sqlc.UpdateSubagentSkillsParams{
|
||||
ID: pgID,
|
||||
Skills: skillsPayload,
|
||||
})
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
return toSubagent(row)
|
||||
}
|
||||
|
||||
func (s *Service) AddSkills(ctx context.Context, id string, req AddSkillsRequest) (Subagent, error) {
|
||||
existing, err := s.Get(ctx, id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
merged := mergeSkills(existing.Skills, req.Skills)
|
||||
payload, err := marshalSkills(merged)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
pgID, err := parseUUID(id)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
row, err := s.queries.UpdateSubagentSkills(ctx, sqlc.UpdateSubagentSkillsParams{
|
||||
ID: pgID,
|
||||
Skills: payload,
|
||||
})
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
return toSubagent(row)
|
||||
}
|
||||
|
||||
func (s *Service) Delete(ctx context.Context, id string) error {
|
||||
pgID, err := parseUUID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.queries.SoftDeleteSubagent(ctx, pgID)
|
||||
}
|
||||
|
||||
func toSubagent(row sqlc.Subagent) (Subagent, error) {
|
||||
messages, err := unmarshalMessages(row.Messages)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
metadata, err := unmarshalMetadata(row.Metadata)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
skills, err := unmarshalSkills(row.Skills)
|
||||
if err != nil {
|
||||
return Subagent{}, err
|
||||
}
|
||||
item := Subagent{
|
||||
ID: toUUIDString(row.ID),
|
||||
Name: row.Name,
|
||||
Description: row.Description,
|
||||
UserID: toUUIDString(row.UserID),
|
||||
Messages: messages,
|
||||
Metadata: metadata,
|
||||
Skills: skills,
|
||||
Deleted: row.Deleted,
|
||||
}
|
||||
if row.CreatedAt.Valid {
|
||||
item.CreatedAt = row.CreatedAt.Time
|
||||
}
|
||||
if row.UpdatedAt.Valid {
|
||||
item.UpdatedAt = row.UpdatedAt.Time
|
||||
}
|
||||
if row.DeletedAt.Valid {
|
||||
deletedAt := row.DeletedAt.Time
|
||||
item.DeletedAt = &deletedAt
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func marshalMessages(messages []map[string]interface{}) ([]byte, error) {
|
||||
if messages == nil {
|
||||
messages = []map[string]interface{}{}
|
||||
}
|
||||
return json.Marshal(messages)
|
||||
}
|
||||
|
||||
func unmarshalMessages(payload []byte) ([]map[string]interface{}, error) {
|
||||
if len(payload) == 0 {
|
||||
return []map[string]interface{}{}, nil
|
||||
}
|
||||
var messages []map[string]interface{}
|
||||
if err := json.Unmarshal(payload, &messages); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if messages == nil {
|
||||
messages = []map[string]interface{}{}
|
||||
}
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func marshalMetadata(metadata map[string]interface{}) ([]byte, error) {
|
||||
if metadata == nil {
|
||||
metadata = map[string]interface{}{}
|
||||
}
|
||||
return json.Marshal(metadata)
|
||||
}
|
||||
|
||||
func unmarshalMetadata(payload []byte) (map[string]interface{}, error) {
|
||||
if len(payload) == 0 {
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
var metadata map[string]interface{}
|
||||
if err := json.Unmarshal(payload, &metadata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if metadata == nil {
|
||||
metadata = map[string]interface{}{}
|
||||
}
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func marshalSkills(skills []string) ([]byte, error) {
|
||||
return json.Marshal(normalizeSkills(skills))
|
||||
}
|
||||
|
||||
func unmarshalSkills(payload []byte) ([]string, error) {
|
||||
if len(payload) == 0 {
|
||||
return []string{}, nil
|
||||
}
|
||||
var skills []string
|
||||
if err := json.Unmarshal(payload, &skills); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if skills == nil {
|
||||
skills = []string{}
|
||||
}
|
||||
return skills, nil
|
||||
}
|
||||
|
||||
func normalizeSkills(skills []string) []string {
|
||||
seen := map[string]struct{}{}
|
||||
normalized := make([]string, 0, len(skills))
|
||||
for _, skill := range skills {
|
||||
trimmed := strings.TrimSpace(skill)
|
||||
if trimmed == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[trimmed]; ok {
|
||||
continue
|
||||
}
|
||||
seen[trimmed] = struct{}{}
|
||||
normalized = append(normalized, trimmed)
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
func mergeSkills(existing []string, incoming []string) []string {
|
||||
merged := append([]string{}, existing...)
|
||||
merged = append(merged, incoming...)
|
||||
return normalizeSkills(merged)
|
||||
}
|
||||
|
||||
func parseUUID(id string) (pgtype.UUID, error) {
|
||||
parsed, err := uuid.Parse(strings.TrimSpace(id))
|
||||
if err != nil {
|
||||
return pgtype.UUID{}, fmt.Errorf("invalid UUID: %w", err)
|
||||
}
|
||||
var pgID pgtype.UUID
|
||||
pgID.Valid = true
|
||||
copy(pgID.Bytes[:], parsed[:])
|
||||
return pgID, nil
|
||||
}
|
||||
|
||||
func toUUIDString(value pgtype.UUID) string {
|
||||
if !value.Valid {
|
||||
return ""
|
||||
}
|
||||
id, err := uuid.FromBytes(value.Bytes[:])
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return id.String()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package subagent
|
||||
|
||||
import "time"
|
||||
|
||||
type Subagent struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
UserID string `json:"user_id"`
|
||||
Messages []map[string]interface{} `json:"messages"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
Skills []string `json:"skills"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Deleted bool `json:"deleted"`
|
||||
DeletedAt *time.Time `json:"deleted_at,omitempty"`
|
||||
}
|
||||
|
||||
type CreateRequest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Messages []map[string]interface{} `json:"messages,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
Skills []string `json:"skills,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateRequest struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateContextRequest struct {
|
||||
Messages []map[string]interface{} `json:"messages"`
|
||||
}
|
||||
|
||||
type UpdateSkillsRequest struct {
|
||||
Skills []string `json:"skills"`
|
||||
}
|
||||
|
||||
type AddSkillsRequest struct {
|
||||
Skills []string `json:"skills"`
|
||||
}
|
||||
|
||||
type ListResponse struct {
|
||||
Items []Subagent `json:"items"`
|
||||
}
|
||||
|
||||
type ContextResponse struct {
|
||||
Messages []map[string]interface{} `json:"messages"`
|
||||
}
|
||||
|
||||
type SkillsResponse struct {
|
||||
Skills []string `json:"skills"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user