mirror of
https://github.com/likelovewant/ollama-for-amd.git
synced 2025-12-22 06:43:57 +00:00
Merge pull request #11705 from ollama/drifkin/fn-schema
tools: support anyOf types
This commit is contained in:
60
api/types.go
60
api/types.go
@@ -225,6 +225,59 @@ func (pt PropertyType) String() string {
|
|||||||
return fmt.Sprintf("%v", []string(pt))
|
return fmt.Sprintf("%v", []string(pt))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ToolProperty struct {
|
||||||
|
AnyOf []ToolProperty `json:"anyOf,omitempty"`
|
||||||
|
Type PropertyType `json:"type"`
|
||||||
|
Items any `json:"items,omitempty"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Enum []any `json:"enum,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTypeScriptType converts a ToolProperty to a TypeScript type string
|
||||||
|
func (tp ToolProperty) ToTypeScriptType() string {
|
||||||
|
if len(tp.AnyOf) > 0 {
|
||||||
|
var types []string
|
||||||
|
for _, anyOf := range tp.AnyOf {
|
||||||
|
types = append(types, anyOf.ToTypeScriptType())
|
||||||
|
}
|
||||||
|
return strings.Join(types, " | ")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tp.Type) == 0 {
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(tp.Type) == 1 {
|
||||||
|
return mapToTypeScriptType(tp.Type[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
var types []string
|
||||||
|
for _, t := range tp.Type {
|
||||||
|
types = append(types, mapToTypeScriptType(t))
|
||||||
|
}
|
||||||
|
return strings.Join(types, " | ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapToTypeScriptType maps JSON Schema types to TypeScript types
|
||||||
|
func mapToTypeScriptType(jsonType string) string {
|
||||||
|
switch jsonType {
|
||||||
|
case "string":
|
||||||
|
return "string"
|
||||||
|
case "number", "integer":
|
||||||
|
return "number"
|
||||||
|
case "boolean":
|
||||||
|
return "boolean"
|
||||||
|
case "array":
|
||||||
|
return "any[]"
|
||||||
|
case "object":
|
||||||
|
return "Record<string, any>"
|
||||||
|
case "null":
|
||||||
|
return "null"
|
||||||
|
default:
|
||||||
|
return "any"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ToolFunction struct {
|
type ToolFunction struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
@@ -233,12 +286,7 @@ type ToolFunction struct {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]ToolProperty `json:"properties"`
|
||||||
Type PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
} `json:"parameters"`
|
} `json:"parameters"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
142
api/types_typescript_test.go
Normal file
142
api/types_typescript_test.go
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToolParameterToTypeScriptType(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
param ToolProperty
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single string type",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"string"},
|
||||||
|
},
|
||||||
|
expected: "string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single number type",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"number"},
|
||||||
|
},
|
||||||
|
expected: "number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "integer maps to number",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"integer"},
|
||||||
|
},
|
||||||
|
expected: "number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "boolean type",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"boolean"},
|
||||||
|
},
|
||||||
|
expected: "boolean",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "array type",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"array"},
|
||||||
|
},
|
||||||
|
expected: "any[]",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "object type",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"object"},
|
||||||
|
},
|
||||||
|
expected: "Record<string, any>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "null type",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"null"},
|
||||||
|
},
|
||||||
|
expected: "null",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple types as union",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"string", "number"},
|
||||||
|
},
|
||||||
|
expected: "string | number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "string or null union",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"string", "null"},
|
||||||
|
},
|
||||||
|
expected: "string | null",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "anyOf with single types",
|
||||||
|
param: ToolProperty{
|
||||||
|
AnyOf: []ToolProperty{
|
||||||
|
{Type: PropertyType{"string"}},
|
||||||
|
{Type: PropertyType{"number"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "string | number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "anyOf with multiple types in each branch",
|
||||||
|
param: ToolProperty{
|
||||||
|
AnyOf: []ToolProperty{
|
||||||
|
{Type: PropertyType{"string", "null"}},
|
||||||
|
{Type: PropertyType{"number"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "string | null | number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested anyOf",
|
||||||
|
param: ToolProperty{
|
||||||
|
AnyOf: []ToolProperty{
|
||||||
|
{Type: PropertyType{"boolean"}},
|
||||||
|
{
|
||||||
|
AnyOf: []ToolProperty{
|
||||||
|
{Type: PropertyType{"string"}},
|
||||||
|
{Type: PropertyType{"number"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "boolean | string | number",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty type returns any",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{},
|
||||||
|
},
|
||||||
|
expected: "any",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unknown type maps to any",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"unknown_type"},
|
||||||
|
},
|
||||||
|
expected: "any",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple types including array",
|
||||||
|
param: ToolProperty{
|
||||||
|
Type: PropertyType{"string", "array", "null"},
|
||||||
|
},
|
||||||
|
expected: "string | any[] | null",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := tt.param.ToTypeScriptType()
|
||||||
|
if result != tt.expected {
|
||||||
|
t.Errorf("ToTypeScriptType() = %q, want %q", result, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -284,21 +284,11 @@ func TestChatMiddleware(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"location"},
|
Required: []string{"location"},
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"location": {
|
"location": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The city and state",
|
Description: "The city and state",
|
||||||
|
|||||||
@@ -392,21 +392,11 @@ func TestGenerateChat(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"location"},
|
Required: []string{"location"},
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"location": {
|
"location": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The city and state",
|
Description: "The city and state",
|
||||||
@@ -493,21 +483,11 @@ func TestGenerateChat(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"location"},
|
Required: []string{"location"},
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"location": {
|
"location": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The city and state",
|
Description: "The city and state",
|
||||||
|
|||||||
@@ -31,21 +31,11 @@ func getTestTools() []api.Tool {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"location"},
|
Required: []string{"location"},
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"location": {
|
"location": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The city and state, e.g. San Francisco, CA",
|
Description: "The city and state, e.g. San Francisco, CA",
|
||||||
@@ -64,21 +54,11 @@ func getTestTools() []api.Tool {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"expression"},
|
Required: []string{"expression"},
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"expression": {
|
"expression": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The mathematical expression to calculate",
|
Description: "The mathematical expression to calculate",
|
||||||
|
|||||||
@@ -127,6 +127,16 @@ var funcs = template.FuncMap{
|
|||||||
// Default format is YYYY-MM-DD
|
// Default format is YYYY-MM-DD
|
||||||
return time.Now().Format("2006-01-02")
|
return time.Now().Format("2006-01-02")
|
||||||
},
|
},
|
||||||
|
"toTypeScriptType": func(v any) string {
|
||||||
|
if param, ok := v.(api.ToolProperty); ok {
|
||||||
|
return param.ToTypeScriptType()
|
||||||
|
}
|
||||||
|
// Handle pointer case
|
||||||
|
if param, ok := v.(*api.ToolProperty); ok && param != nil {
|
||||||
|
return param.ToTypeScriptType()
|
||||||
|
}
|
||||||
|
return "any"
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Parse(s string) (*Template, error) {
|
func Parse(s string) (*Template, error) {
|
||||||
|
|||||||
@@ -45,21 +45,11 @@ func TestParser(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Required: []string{"city"},
|
Required: []string{"city"},
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"format": {
|
"format": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The format to return the temperature in",
|
Description: "The format to return the temperature in",
|
||||||
@@ -83,20 +73,10 @@ func TestParser(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"location": {
|
"location": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The location to get the weather conditions for",
|
Description: "The location to get the weather conditions for",
|
||||||
@@ -129,20 +109,10 @@ func TestParser(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"location": {
|
"location": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The location to get the address for",
|
Description: "The location to get the address for",
|
||||||
@@ -161,20 +131,10 @@ func TestParser(t *testing.T) {
|
|||||||
Defs any `json:"$defs,omitempty"`
|
Defs any `json:"$defs,omitempty"`
|
||||||
Items any `json:"items,omitempty"`
|
Items any `json:"items,omitempty"`
|
||||||
Required []string `json:"required"`
|
Required []string `json:"required"`
|
||||||
Properties map[string]struct {
|
Properties map[string]api.ToolProperty `json:"properties"`
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
} `json:"properties"`
|
|
||||||
}{
|
}{
|
||||||
Type: "object",
|
Type: "object",
|
||||||
Properties: map[string]struct {
|
Properties: map[string]api.ToolProperty{
|
||||||
Type api.PropertyType `json:"type"`
|
|
||||||
Items any `json:"items,omitempty"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Enum []any `json:"enum,omitempty"`
|
|
||||||
}{
|
|
||||||
"a": {
|
"a": {
|
||||||
Type: api.PropertyType{"string"},
|
Type: api.PropertyType{"string"},
|
||||||
Description: "The first number to add",
|
Description: "The first number to add",
|
||||||
|
|||||||
Reference in New Issue
Block a user