| package schema_test |
|
|
| import ( |
| "encoding/json" |
|
|
| . "github.com/mudler/LocalAI/core/schema" |
|
|
| . "github.com/onsi/ginkgo/v2" |
| . "github.com/onsi/gomega" |
| ) |
|
|
| var _ = Describe("LLM tests", func() { |
|
|
| Context("ToProtoMessages conversion", func() { |
| It("should convert basic message with string content", func() { |
| messages := Messages{ |
| { |
| Role: "user", |
| Content: "Hello, world!", |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("user")) |
| Expect(protoMessages[0].Content).To(Equal("Hello, world!")) |
| Expect(protoMessages[0].Name).To(BeEmpty()) |
| Expect(protoMessages[0].ToolCalls).To(BeEmpty()) |
| }) |
|
|
| It("should convert message with nil content to empty string", func() { |
| messages := Messages{ |
| { |
| Role: "assistant", |
| Content: nil, |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("assistant")) |
| Expect(protoMessages[0].Content).To(Equal("")) |
| }) |
|
|
| It("should convert message with array content (multimodal)", func() { |
| messages := Messages{ |
| { |
| Role: "user", |
| Content: []interface{}{ |
| map[string]interface{}{ |
| "type": "text", |
| "text": "Hello", |
| }, |
| map[string]interface{}{ |
| "type": "text", |
| "text": " World", |
| }, |
| }, |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("user")) |
| Expect(protoMessages[0].Content).To(Equal("Hello World")) |
| }) |
|
|
| It("should convert message with tool_calls", func() { |
| messages := Messages{ |
| { |
| Role: "assistant", |
| Content: "I'll call a function", |
| ToolCalls: []ToolCall{ |
| { |
| Index: 0, |
| ID: "call_123", |
| Type: "function", |
| FunctionCall: FunctionCall{ |
| Name: "get_weather", |
| Arguments: `{"location": "San Francisco"}`, |
| }, |
| }, |
| }, |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("assistant")) |
| Expect(protoMessages[0].Content).To(Equal("I'll call a function")) |
| Expect(protoMessages[0].ToolCalls).NotTo(BeEmpty()) |
|
|
| |
| var toolCalls []ToolCall |
| err := json.Unmarshal([]byte(protoMessages[0].ToolCalls), &toolCalls) |
| Expect(err).NotTo(HaveOccurred()) |
| Expect(toolCalls).To(HaveLen(1)) |
| Expect(toolCalls[0].ID).To(Equal("call_123")) |
| Expect(toolCalls[0].FunctionCall.Name).To(Equal("get_weather")) |
| }) |
|
|
| It("should convert message with name field", func() { |
| messages := Messages{ |
| { |
| Role: "tool", |
| Content: "Function result", |
| Name: "get_weather", |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("tool")) |
| Expect(protoMessages[0].Content).To(Equal("Function result")) |
| Expect(protoMessages[0].Name).To(Equal("get_weather")) |
| }) |
|
|
| It("should convert message with tool_calls and nil content", func() { |
| messages := Messages{ |
| { |
| Role: "assistant", |
| Content: nil, |
| ToolCalls: []ToolCall{ |
| { |
| Index: 0, |
| ID: "call_456", |
| Type: "function", |
| FunctionCall: FunctionCall{ |
| Name: "search", |
| Arguments: `{"query": "test"}`, |
| }, |
| }, |
| }, |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("assistant")) |
| Expect(protoMessages[0].Content).To(Equal("")) |
| Expect(protoMessages[0].ToolCalls).NotTo(BeEmpty()) |
|
|
| var toolCalls []ToolCall |
| err := json.Unmarshal([]byte(protoMessages[0].ToolCalls), &toolCalls) |
| Expect(err).NotTo(HaveOccurred()) |
| Expect(toolCalls).To(HaveLen(1)) |
| Expect(toolCalls[0].FunctionCall.Name).To(Equal("search")) |
| }) |
|
|
| It("should convert multiple messages", func() { |
| messages := Messages{ |
| { |
| Role: "user", |
| Content: "Hello", |
| }, |
| { |
| Role: "assistant", |
| Content: "Hi there!", |
| }, |
| { |
| Role: "user", |
| Content: "How are you?", |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(3)) |
| Expect(protoMessages[0].Role).To(Equal("user")) |
| Expect(protoMessages[0].Content).To(Equal("Hello")) |
| Expect(protoMessages[1].Role).To(Equal("assistant")) |
| Expect(protoMessages[1].Content).To(Equal("Hi there!")) |
| Expect(protoMessages[2].Role).To(Equal("user")) |
| Expect(protoMessages[2].Content).To(Equal("How are you?")) |
| }) |
|
|
| It("should handle empty messages slice", func() { |
| messages := Messages{} |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(0)) |
| }) |
|
|
| It("should handle message with all optional fields", func() { |
| messages := Messages{ |
| { |
| Role: "assistant", |
| Content: "I'll help you", |
| Name: "test_tool", |
| ToolCalls: []ToolCall{ |
| { |
| Index: 0, |
| ID: "call_789", |
| Type: "function", |
| FunctionCall: FunctionCall{ |
| Name: "test_function", |
| Arguments: `{"param": "value"}`, |
| }, |
| }, |
| }, |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("assistant")) |
| Expect(protoMessages[0].Content).To(Equal("I'll help you")) |
| Expect(protoMessages[0].Name).To(Equal("test_tool")) |
| Expect(protoMessages[0].ToolCalls).NotTo(BeEmpty()) |
|
|
| var toolCalls []ToolCall |
| err := json.Unmarshal([]byte(protoMessages[0].ToolCalls), &toolCalls) |
| Expect(err).NotTo(HaveOccurred()) |
| Expect(toolCalls).To(HaveLen(1)) |
| }) |
|
|
| It("should handle message with empty string content", func() { |
| messages := Messages{ |
| { |
| Role: "user", |
| Content: "", |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("user")) |
| Expect(protoMessages[0].Content).To(Equal("")) |
| }) |
|
|
| It("should handle message with array content containing non-text parts", func() { |
| messages := Messages{ |
| { |
| Role: "user", |
| Content: []interface{}{ |
| map[string]interface{}{ |
| "type": "text", |
| "text": "Hello", |
| }, |
| map[string]interface{}{ |
| "type": "image", |
| "url": "https://example.com/image.jpg", |
| }, |
| }, |
| }, |
| } |
|
|
| protoMessages := messages.ToProto() |
|
|
| Expect(protoMessages).To(HaveLen(1)) |
| Expect(protoMessages[0].Role).To(Equal("user")) |
| |
| Expect(protoMessages[0].Content).To(Equal("Hello")) |
| }) |
| }) |
| }) |
|
|