Skip to content
1 change: 1 addition & 0 deletions go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/leanovate/gopter v0.2.11 // indirect
go.opencensus.io v0.24.0 // indirect
)

Expand Down
477 changes: 477 additions & 0 deletions go/go.sum

Large diffs are not rendered by default.

130 changes: 130 additions & 0 deletions go/plugins/ollama/INTEGRATION_TESTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Ollama Integration Tests

This directory contains integration tests for the Ollama plugin's structured output feature. These tests require a running Ollama instance and are tagged with `integration` to separate them from unit tests.

## Prerequisites

1. **Ollama must be running**: Install and start Ollama from https://ollama.com
2. **Models must be available**: Pull at least one model that supports structured output

### Recommended Models

For best results, use models that support structured output:
- `llama3.2` (default for both chat and generate)
- `llama3.1`
- `qwen2.5`
- `mistral`

Pull a model:
```bash
ollama pull llama3.2
```

## Running Integration Tests

### Run all integration tests:
```bash
go test -tags=integration -v ./go/plugins/ollama/...
```

### Run specific integration test:
```bash
go test -tags=integration -v -run TestIntegration_ChatModelWithSchema ./go/plugins/ollama/
```

### Run with custom configuration:
```bash
# Use custom Ollama server address
OLLAMA_SERVER_ADDRESS=http://localhost:11434 go test -tags=integration -v ./go/plugins/ollama/

# Use specific models
OLLAMA_CHAT_MODEL=llama3.1 OLLAMA_GENERATE_MODEL=llama3.1 go test -tags=integration -v ./go/plugins/ollama/
```

## Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `OLLAMA_SERVER_ADDRESS` | Ollama server URL | `http://localhost:11434` |
| `OLLAMA_CHAT_MODEL` | Model to use for chat tests | `llama3.2` |
| `OLLAMA_GENERATE_MODEL` | Model to use for generate tests | `llama3.2` |

## Test Coverage

The integration tests validate:

### 8.1 Chat Model with Schema (TestIntegration_ChatModelWithSchema)
- Makes actual API call to Ollama chat endpoint with schema
- Verifies response conforms to schema
- **Validates Requirements**: 1.1, 1.2, 1.3, 4.1, 4.2

### 8.2 Generate Model with Schema (TestIntegration_GenerateModelWithSchema)
- Makes actual API call to Ollama generate endpoint with schema
- Verifies response conforms to schema
- **Validates Requirements**: 1.1, 1.2, 1.4, 4.1, 4.2

### 8.3 Schema-less JSON Mode (TestIntegration_SchemalessJSONMode)
- Makes API calls with format: "json" and no schema
- Verifies responses are valid JSON
- **Validates Requirements**: 2.1, 2.2

### 8.4 Streaming with Schemas (TestIntegration_StreamingWithSchema)
- Makes streaming API calls with schemas
- Verifies chunks are parsed correctly
- Verifies final merged output is complete
- **Validates Requirements**: 5.1, 5.2, 5.3, 5.4

### 8.5 Error Scenarios (TestIntegration_ErrorScenarios)
- Tests Ollama API error responses
- Tests invalid model names
- Verifies error messages are properly propagated
- **Validates Requirements**: 6.1, 6.4

## Troubleshooting

### Tests are skipped
If tests are skipped with messages like "Ollama not available", ensure:
1. Ollama is running: `ollama serve`
2. The server address is correct
3. The firewall allows connections to Ollama

### Model not found
If tests are skipped with "Model not available":
1. Pull the required model: `ollama pull llama3.2`
2. Or specify a different model using environment variables

### Tests timeout
If tests timeout:
1. Ensure your machine has sufficient resources
2. Try using a smaller/faster model
3. Increase the timeout in the test code if needed

### Connection refused
If you see "connection refused" errors:
1. Check Ollama is running: `curl http://localhost:11434/api/tags`
2. Verify the server address matches your Ollama configuration
3. Check for firewall or network issues

## CI/CD Integration

To run integration tests in CI/CD:

```yaml
# Example GitHub Actions workflow
- name: Start Ollama
run: |
curl -fsSL https://ollama.com/install.sh | sh
ollama serve &
sleep 5
ollama pull llama3.2

- name: Run Integration Tests
run: go test -tags=integration -v ./go/plugins/ollama/...
```

## Notes

- Integration tests make real API calls and may take several seconds to complete
- Tests require network access to the Ollama server
- Some tests may produce different outputs depending on the model used
- The tests validate structure and format, not specific content
228 changes: 228 additions & 0 deletions go/plugins/ollama/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# Ollama Plugin for Genkit Go

The Ollama plugin enables Genkit Go applications to use locally-hosted [Ollama](https://ollama.com/) models for text generation, structured output, tool calling, and more.

## Installation

```bash
go get github.com/firebase/genkit/go/plugins/ollama
```

## Setup

1. Install Ollama from [ollama.com](https://ollama.com/)
2. Pull a model: `ollama pull llama3.1`
3. Start the Ollama server (usually runs automatically on `http://localhost:11434`)

## Basic Usage

```go
import (
"github.com/firebase/genkit/go/genkit"
"github.com/firebase/genkit/go/plugins/ollama"
)

func main() {
ctx := context.Background()

// Initialize the Ollama plugin
ollamaPlugin := &ollama.Ollama{
ServerAddress: "http://localhost:11434",
Timeout: 60,
}

g := genkit.Init(ctx, genkit.WithPlugins(ollamaPlugin))

// Define a model
model := ollamaPlugin.DefineModel(g,
ollama.ModelDefinition{
Name: "llama3.1",
Type: "chat",
},
nil)

// Generate text
resp, _ := genkit.Generate(ctx, g,
ai.WithModel(model),
ai.WithMessages(ai.NewUserTextMessage("Hello!")),
)

fmt.Println(resp.Text())
}
```

## Structured Output

The Ollama plugin supports structured output through Ollama's native [structured output capability](https://docs.ollama.com/capabilities/structured-outputs). This feature allows you to constrain model responses to match specific JSON schemas, ensuring reliable extraction of structured data.

### Schema-Based Structured Output

Use a JSON schema to enforce a specific structure on the model's response:

```go
import (
"encoding/json"
"github.com/invopop/jsonschema"
)

// Define your output structure
type Person struct {
Name string `json:"name" jsonschema:"required"`
Age int `json:"age" jsonschema:"required"`
Occupation string `json:"occupation" jsonschema:"required"`
Hobbies []string `json:"hobbies" jsonschema:"required"`
}

// Generate a JSON schema from the struct
reflector := jsonschema.Reflector{
AllowAdditionalProperties: false,
DoNotReference: true,
}
schema := reflector.Reflect(&Person{})
schemaBytes, _ := json.Marshal(schema)

var schemaMap map[string]any
json.Unmarshal(schemaBytes, &schemaMap)

// Request structured output
resp, _ := genkit.Generate(ctx, g,
ai.WithModel(model),
ai.WithMessages(ai.NewUserTextMessage("Generate info about a software engineer")),
ai.WithOutputConfig(&ai.ModelOutputConfig{
Format: "json",
Schema: schemaMap,
}),
)

// Parse the structured response
var person Person
json.Unmarshal([]byte(resp.Text()), &person)
fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
```

**See full example:** [samples/ollama-structured](../../samples/ollama-structured)

### Schema-less JSON Mode

Request generic JSON output without enforcing a specific structure:

```go
resp, _ := genkit.Generate(ctx, g,
ai.WithModel(model),
ai.WithMessages(ai.NewUserTextMessage("List 3 programming languages as JSON")),
ai.WithOutputConfig(&ai.ModelOutputConfig{
Format: "json",
// No Schema specified - model returns valid JSON in any structure
}),
)

// Parse as generic JSON
var result map[string]any
json.Unmarshal([]byte(resp.Text()), &result)
```

**See full example:** [samples/ollama-json-mode](../../samples/ollama-json-mode)

### When to Use Each Mode

**Schema-based structured output:**
- You need guaranteed structure for reliable parsing
- You want type-safe Go structs
- Building production systems with strict data requirements
- Extracting specific fields from model responses

**Schema-less JSON mode:**
- You want valid JSON but don't need strict structure enforcement
- The output structure varies based on the prompt
- Prototyping and want flexibility
- Handling dynamic JSON structures in your application

### Requirements and Limitations

- **Model Support:** Structured output works with most recent Ollama models. Older models may not support this feature.
- **Ollama Version:** Requires Ollama version that supports the `format` parameter (most recent versions).
- **Schema Format:** Schemas must be valid JSON Schema objects.
- **No Client-Side Validation:** The plugin does not validate responses against the schema. Ollama is responsible for schema enforcement.
- **Error Handling:** If schema serialization fails, an error is returned before making the API request.

## Tool Calling

The Ollama plugin supports tool calling for chat models:

```go
weatherTool := genkit.DefineTool(g, "weather", "Get weather for a location",
func(ctx *ai.ToolContext, input WeatherInput) (WeatherData, error) {
// Implementation
return getWeather(input.Location), nil
},
)

resp, _ := genkit.Generate(ctx, g,
ai.WithModel(model),
ai.WithMessages(ai.NewUserTextMessage("What's the weather in Tokyo?")),
ai.WithTools(weatherTool),
)
```

**See full example:** [samples/ollama-tools](../../samples/ollama-tools)

## Vision Models

Some Ollama models support image inputs:

```go
imageData, _ := os.ReadFile("image.jpg")
imagePart := ai.NewMediaPart("image/jpeg", string(imageData))

resp, _ := genkit.Generate(ctx, g,
ai.WithModel(model),
ai.WithMessages(ai.NewUserMessage(
ai.NewTextPart("What's in this image?"),
imagePart,
)),
)
```

**See full example:** [samples/ollama-vision](../../samples/ollama-vision)

## Configuration

### Plugin Options

```go
ollamaPlugin := &ollama.Ollama{
ServerAddress: "http://localhost:11434", // Ollama server URL
Timeout: 60, // Request timeout in seconds
}
```

### Model Types

- `"chat"`: For conversational models (supports tools and multi-turn conversations)
- `"generate"`: For completion models (single-turn text generation)

### Supported Models

The plugin works with any model available in Ollama. Popular choices include:

- `llama3.1`, `llama3.2` - Meta's Llama models
- `mistral`, `mixtral` - Mistral AI models
- `phi3` - Microsoft's Phi models
- `qwen2` - Alibaba's Qwen models
- `gemma2` - Google's Gemma models

Check [ollama.com/library](https://ollama.com/library) for the full list.

## Additional Resources

- [Ollama Documentation](https://docs.ollama.com/)
- [Ollama Structured Outputs](https://docs.ollama.com/capabilities/structured-outputs)
- [Genkit Go Documentation](https://genkit.dev/docs/overview/?lang=go)
- [Ollama Model Library](https://ollama.com/library)

## Examples

- [ollama-structured](../../samples/ollama-structured) - Schema-based structured output
- [ollama-json-mode](../../samples/ollama-json-mode) - Schema-less JSON mode
- [ollama-tools](../../samples/ollama-tools) - Tool calling with Ollama
- [ollama-vision](../../samples/ollama-vision) - Vision models with image inputs
Loading