mirror of
https://github.com/hanzoai/agent.git
synced 2026-09-11 21:08:51 +00:00
The agent IS github.com/hanzoai/agent — package agent at repo root. POST /v1/agent + presets + per-org conversation history (zip+orm). Importable directly by ai/cloud.
152 lines
5.7 KiB
Go
152 lines
5.7 KiB
Go
package agent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"sort"
|
|
"strings"
|
|
|
|
openai "github.com/sashabaranov/go-openai"
|
|
)
|
|
|
|
// Preset is one named agent type in the preset LIBRARY — a first-class, extensible
|
|
// catalog the /v1/agent API creates rounds from and lists at GET /v1/agent/presets.
|
|
// It frames a tool-calling round: the system prompt that instructs the model, the
|
|
// builtin tool defs offered alongside the caller's and the org's registered tools,
|
|
// and whether the model's tool calls are EXECUTED server-side (registry Dispatch →
|
|
// actions) or handed back to the client as ops (a graph/UI mutation the server
|
|
// cannot perform). Adding an agent type is one Register call — no other change.
|
|
type Preset struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
SystemPrompt string `json:"systemPrompt"`
|
|
BuiltinTools []openai.Tool `json:"-"`
|
|
// ServerExecuted gates the tool-call split: when true, a call the tool
|
|
// registry knows is dispatched server-side and reported in actions; when
|
|
// false the round is advisory — every call is returned as an op for the
|
|
// client to apply.
|
|
ServerExecuted bool `json:"serverExecuted"`
|
|
}
|
|
|
|
// DefaultPreset is used when a request omits preset (and its capability alias).
|
|
const DefaultPreset = "graph"
|
|
|
|
// presets is the library. Seeded with graph + create below; a host extends it by
|
|
// calling Register before Mount.
|
|
var presets = map[string]Preset{}
|
|
|
|
// Register adds (or replaces) a preset in the library. Call from init() or before
|
|
// Mount. Replacing an existing id is allowed so a host can re-skin a builtin.
|
|
func Register(p Preset) {
|
|
presets[strings.TrimSpace(p.ID)] = p
|
|
}
|
|
|
|
// Presets returns every registered preset, sorted by ID for a stable listing.
|
|
func Presets() []Preset {
|
|
out := make([]Preset, 0, len(presets))
|
|
for _, p := range presets {
|
|
out = append(out, p)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].ID < out[j].ID })
|
|
return out
|
|
}
|
|
|
|
// presetFor resolves a preset by id, defaulting an empty id to DefaultPreset. ok
|
|
// is false for an unknown id so the handler answers 400 rather than guessing.
|
|
func presetFor(id string) (Preset, bool) {
|
|
id = strings.TrimSpace(id)
|
|
if id == "" {
|
|
id = DefaultPreset
|
|
}
|
|
p, ok := presets[id]
|
|
return p, ok
|
|
}
|
|
|
|
// toolDef builds an OpenAI function tool from a JSON-Schema string. The schema is
|
|
// held as json.RawMessage so it reaches the model verbatim.
|
|
func toolDef(name, desc, schema string) openai.Tool {
|
|
return openai.Tool{
|
|
Type: openai.ToolTypeFunction,
|
|
Function: &openai.FunctionDefinition{
|
|
Name: name,
|
|
Description: desc,
|
|
Parameters: json.RawMessage(schema),
|
|
},
|
|
}
|
|
}
|
|
|
|
func init() {
|
|
Register(Preset{
|
|
ID: "graph",
|
|
Title: "Studio Graph Copilot",
|
|
ServerExecuted: false,
|
|
SystemPrompt: strings.TrimSpace(`
|
|
You are Hanzo Studio Copilot. You read and mutate a node workflow graph
|
|
(LiteGraph / ComfyUI-compatible). The user describes an outcome; you respond with
|
|
tool calls that describe the graph operations to apply. You never execute the
|
|
graph yourself — the Studio client applies each operation and re-queues the graph.
|
|
Reference nodes you create by the id you give them in add_node. Prefer the fewest
|
|
operations that achieve the request; explain briefly in text when helpful.`),
|
|
BuiltinTools: []openai.Tool{
|
|
toolDef("add_node", "Add a node to the graph.", `{
|
|
"type":"object",
|
|
"properties":{
|
|
"id":{"type":"string","description":"caller label to reference this node in later ops"},
|
|
"type":{"type":"string","description":"node class, e.g. KSampler"},
|
|
"pos":{"type":"array","items":{"type":"number"},"description":"[x,y] canvas position"}
|
|
},
|
|
"required":["type"]
|
|
}`),
|
|
toolDef("set_widget", "Set a widget value on a node.", `{
|
|
"type":"object",
|
|
"properties":{
|
|
"node_id":{"type":"string"},
|
|
"name":{"type":"string","description":"widget name"},
|
|
"value":{}
|
|
},
|
|
"required":["node_id","name","value"]
|
|
}`),
|
|
toolDef("set_prompt", "Set the prompt text on a node.", `{
|
|
"type":"object",
|
|
"properties":{"node_id":{"type":"string"},"text":{"type":"string"}},
|
|
"required":["node_id","text"]
|
|
}`),
|
|
toolDef("connect", "Connect an output slot to an input slot.", `{
|
|
"type":"object",
|
|
"properties":{
|
|
"from":{"type":"string","description":"source node id"},
|
|
"from_slot":{"description":"output slot index or name"},
|
|
"to":{"type":"string","description":"target node id"},
|
|
"to_slot":{"description":"input slot index or name"}
|
|
},
|
|
"required":["from","to"]
|
|
}`),
|
|
toolDef("move_node", "Move a node to a new position.", `{
|
|
"type":"object",
|
|
"properties":{"node_id":{"type":"string"},"pos":{"type":"array","items":{"type":"number"}}},
|
|
"required":["node_id","pos"]
|
|
}`),
|
|
toolDef("delete_node", "Remove a node from the graph.", `{
|
|
"type":"object",
|
|
"properties":{"node_id":{"type":"string"}},
|
|
"required":["node_id"]
|
|
}`),
|
|
toolDef("layout", "Auto-arrange the graph.", `{"type":"object","properties":{}}`),
|
|
toolDef("queue", "Queue the graph for execution.", `{"type":"object","properties":{}}`),
|
|
},
|
|
})
|
|
|
|
Register(Preset{
|
|
ID: "create",
|
|
Title: "Product & Fashion Create",
|
|
ServerExecuted: true,
|
|
SystemPrompt: strings.TrimSpace(`
|
|
You are Hanzo Create, a fashion and product render assistant. You help the user
|
|
generate, edit, and refine product and fashion imagery. Use the tools available to
|
|
this workspace — the org's connected render and MCP services — to run and iterate
|
|
on renders. Call a tool when it advances the request; describe the result briefly.`),
|
|
// No builtin tools: the callable set is the org's registered MCP/registry
|
|
// tools, resolved per request and dispatched server-side.
|
|
BuiltinTools: nil,
|
|
})
|
|
}
|