diff --git a/neo/assistant/api.go b/neo/assistant/api.go
index a900c49e..59a00e44 100644
--- a/neo/assistant/api.go
+++ b/neo/assistant/api.go
@@ -561,7 +561,7 @@ func (ast *Assistant) saveChatHistory(ctx chatctx.Context, messages []chatMessag
{
"role": "assistant",
"content": contents.JSON(),
- "name": ctx.Sid,
+ "name": ast.ID,
"assistant_id": ast.ID,
"assistant_name": ast.Name,
"assistant_avatar": ast.Avatar,
@@ -605,7 +605,7 @@ func (ast *Assistant) withOptions(options map[string]interface{}) map[string]int
func (ast *Assistant) withPrompts(messages []chatMessage.Message) []chatMessage.Message {
if ast.Prompts != nil {
for _, prompt := range ast.Prompts {
- name := ast.Name
+ name := strings.ReplaceAll(ast.ID, ".", "_") // OpenAI only supports underscore in the name
if prompt.Name != "" {
name = prompt.Name
}
@@ -618,43 +618,61 @@ func (ast *Assistant) withPrompts(messages []chatMessage.Message) []chatMessage.
settings, has := connectorSettings[ast.Connector]
if !has || !settings.Tools {
raw, _ := jsoniter.MarshalToString(ast.Tools.Tools)
- messages = append(messages, *chatMessage.New().Map(map[string]interface{}{
- "role": "system",
- "name": "TOOL_CALLS_SCHEMA",
- "content": raw,
- }))
- messages = append(messages, *chatMessage.New().Map(map[string]interface{}{
- "role": "system",
- "name": "TOOL_CALLS",
- "content": "## Tool Response Format\n" +
- "1. If no matching function exists in TOOL_CALLS_SCHEMA, respond normally without using tool calls\n" +
- "2. When using tools, wrap function calls in and tags\n" +
- "3. The tool call must be a valid JSON object\n" +
- "4. Follow the JSON Schema defined in TOOL_CALLS_SCHEMA\n" +
- "5. One complete tool call per response\n" +
- "6. Parameter values MUST strictly follow the descriptions and validation rules defined in properties\n" +
- "7. For each parameter, carefully check and comply with:\n" +
- " - Data type requirements\n" +
- " - Format restrictions\n" +
- " - Value range limitations\n" +
- " - Pattern matching rules\n" +
- " - Required field validations\n\n" +
- "Example:\n" +
- "\n" + `{"function":"","arguments":{"":""}}` + "\n",
- }))
- messages = append(messages, *chatMessage.New().Map(map[string]interface{}{
- "role": "system",
- "name": "TOOL_CALLS",
- "content": "## Tool Usage Guidelines\n" +
- "1. Use functions defined in TOOL_CALLS_SCHEMA only when they match your needs\n" +
- "2. If no matching function exists, respond normally as a helpful assistant\n" +
- "3. When using tools, arguments must match the schema definition exactly\n" +
- "4. All parameter values must strictly adhere to the validation rules specified in properties\n" +
- "5. Never skip or ignore any validation requirements defined in the schema",
- }))
+ examples := []string{}
+ for _, tool := range ast.Tools.Tools {
+ example := tool.Example()
+ examples = append(examples, example)
+ }
- // Add tool_calls prompts
+ examplesStr := ""
+ if len(examples) > 0 {
+ examplesStr = "Examples:\n" + strings.Join(examples, "\n\n")
+ }
+
+ prompts := []map[string]interface{}{
+ {
+ "role": "system",
+ "name": "TOOL_CALLS_SCHEMA",
+ "content": raw,
+ },
+ {
+ "role": "system",
+ "name": "TOOL_CALLS_SCHEMA",
+ "content": "## Tool Calls Schema Definition\n" +
+ "Each tool call is defined with:\n" +
+ " - type: always 'function'\n" +
+ " - function:\n" +
+ " - name: function name\n" +
+ " - description: function description\n" +
+ " - parameters: function parameters with type and validation rules\n",
+ },
+ {
+ "role": "system",
+ "name": "TOOL_CALLS",
+ "content": "## Tool Response Format\n" +
+ "1. Only use tool calls when a function matches your task exactly\n" +
+ "2. Each tool call must be wrapped in and tags\n" +
+ "3. Tool call must be a valid JSON with:\n" +
+ " {\"function\": \"function_name\", \"arguments\": {parameters}}\n" +
+ "4. Return the function's result as your response\n" +
+ "5. One tool call per response\n" +
+ "6. Arguments must match parameter types, rules and description\n\n" +
+ examplesStr,
+ },
+ {
+ "role": "system",
+ "name": "TOOL_CALLS",
+ "content": "## Tool Usage Guidelines\n" +
+ "1. Use functions defined in TOOL_CALLS_SCHEMA only when they match your needs\n" +
+ "2. If no matching function exists, respond normally as a helpful assistant\n" +
+ "3. When using tools, arguments must match the schema definition exactly\n" +
+ "4. All parameter values must strictly adhere to the validation rules specified in properties\n" +
+ "5. Never skip or ignore any validation requirements defined in the schema",
+ },
+ }
+
+ // Add tool_calls developer prompts
if ast.Tools.Prompts != nil && len(ast.Tools.Prompts) > 0 {
for _, prompt := range ast.Tools.Prompts {
messages = append(messages, *chatMessage.New().Map(map[string]interface{}{
@@ -664,6 +682,12 @@ func (ast *Assistant) withPrompts(messages []chatMessage.Message) []chatMessage.
}))
}
}
+
+ // Add the prompts
+ for _, prompt := range prompts {
+ messages = append(messages, *chatMessage.New().Map(prompt))
+ }
+
}
}
diff --git a/neo/assistant/tool.go b/neo/assistant/tool.go
new file mode 100644
index 00000000..60fc7579
--- /dev/null
+++ b/neo/assistant/tool.go
@@ -0,0 +1,73 @@
+package assistant
+
+import (
+ "fmt"
+
+ jsoniter "github.com/json-iterator/go"
+)
+
+// Tool represents a tool
+type Tool struct {
+ Type string `json:"type"`
+ Function struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Parameters Parameter `json:"parameters"`
+ Strict bool `json:"strict,omitempty"`
+ } `json:"function"`
+}
+
+// SchemaProperty represents a JSON Schema property
+type SchemaProperty struct {
+ Type string `json:"type,omitempty"`
+ Description string `json:"description,omitempty"`
+}
+
+// Parameter represents the parameters field in function calling format
+type Parameter struct {
+ Type string `json:"type"`
+ Properties map[string]SchemaProperty `json:"properties,omitempty"`
+ Required []string `json:"required,omitempty"`
+ AdditionalProperties bool `json:"additionalProperties"`
+}
+
+// Example returns a formatted example of how to use this tool
+func (tool Tool) Example() string {
+ return fmt.Sprintf("\n{\"function\":\"%s\",\"arguments\":%s}\n",
+ tool.Function.Name,
+ jsoniter.Wrap(tool.ExampleArguments()).ToString())
+}
+
+// ExampleArguments generates example arguments for the tool based on parameter types
+func (tool Tool) ExampleArguments() map[string]interface{} {
+
+ args := map[string]interface{}{}
+
+ // Handle the root parameter object
+ if tool.Function.Parameters.Type == "object" && tool.Function.Parameters.Properties != nil {
+ for name, prop := range tool.Function.Parameters.Properties {
+ args[name] = generateExampleValue(name, prop)
+ }
+ }
+ return args
+}
+
+// generateExampleValue creates an example value for a parameter
+func generateExampleValue(name string, prop SchemaProperty) interface{} {
+ switch prop.Type {
+ case "string":
+ return fmt.Sprintf("<%s:string>", name)
+ case "number":
+ return fmt.Sprintf("<%s:number>", name)
+ case "integer":
+ return fmt.Sprintf("<%s:integer>", name)
+ case "boolean":
+ return fmt.Sprintf("<%s:boolean>", name)
+ case "object":
+ return fmt.Sprintf("<%s:object>", name)
+ case "array":
+ return fmt.Sprintf("<%s:array>", name)
+ default:
+ return fmt.Sprintf("<%s>", name)
+ }
+}
diff --git a/neo/assistant/types.go b/neo/assistant/types.go
index 712ccede..cad8fd16 100644
--- a/neo/assistant/types.go
+++ b/neo/assistant/types.go
@@ -94,16 +94,6 @@ type Prompt struct {
Name string `json:"name,omitempty"`
}
-// Tool represents a tool
-type Tool struct {
- Type string `json:"type"`
- Function struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Parameters map[string]interface{} `json:"parameters"`
- } `json:"function"`
-}
-
// QueryParam the assistant query param
type QueryParam struct {
Limit uint `json:"limit"`