Initial commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
# Flows & HTTP
|
||||
|
||||
## DefineFlow
|
||||
|
||||
Wrap AI logic in a flow for observability, tracing, and HTTP deployment.
|
||||
|
||||
```go
|
||||
jokeFlow := genkit.DefineFlow(g, "jokeFlow",
|
||||
func(ctx context.Context, topic string) (string, error) {
|
||||
return genkit.GenerateText(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a joke about %s", topic),
|
||||
)
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### Running a Flow Directly
|
||||
|
||||
```go
|
||||
result, err := jokeFlow.Run(ctx, "cats")
|
||||
```
|
||||
|
||||
## DefineStreamingFlow
|
||||
|
||||
Flows that stream chunks back to the caller. Two common patterns:
|
||||
|
||||
### Pattern 1: Passthrough Streaming
|
||||
|
||||
Pass the stream callback directly through to `WithStreaming`. The callback type is `ai.ModelStreamCallback` = `func(context.Context, *ai.ModelResponseChunk) error`:
|
||||
|
||||
```go
|
||||
genkit.DefineStreamingFlow(g, "streamingJokeFlow",
|
||||
func(ctx context.Context, topic string, sendChunk ai.ModelStreamCallback) (string, error) {
|
||||
resp, err := genkit.Generate(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a long joke about %s", topic),
|
||||
ai.WithStreaming(sendChunk), // passthrough
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.Text(), nil
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 2: Manual String Streaming
|
||||
|
||||
Use `core.StreamCallback[string]` to stream extracted text:
|
||||
|
||||
```go
|
||||
genkit.DefineStreamingFlow(g, "streamingJokeFlow",
|
||||
func(ctx context.Context, topic string, sendChunk core.StreamCallback[string]) (string, error) {
|
||||
stream := genkit.GenerateStream(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a long joke about %s", topic),
|
||||
)
|
||||
for result, err := range stream {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if result.Done {
|
||||
return result.Response.Text(), nil
|
||||
}
|
||||
sendChunk(ctx, result.Chunk.Text())
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### Typed Streaming Flows
|
||||
|
||||
Use `core.StreamCallback[T]` with `GenerateDataStream` for typed chunks:
|
||||
|
||||
```go
|
||||
genkit.DefineStreamingFlow(g, "structuredStream",
|
||||
func(ctx context.Context, input JokeRequest, sendChunk core.StreamCallback[*Joke]) (*Joke, error) {
|
||||
stream := genkit.GenerateDataStream[*Joke](ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a joke about %s", input.Topic),
|
||||
)
|
||||
for result, err := range stream {
|
||||
if err != nil { return nil, err }
|
||||
if result.Done { return result.Output, nil }
|
||||
sendChunk(ctx, result.Chunk)
|
||||
}
|
||||
return nil, nil
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Named Sub-Steps
|
||||
|
||||
Use `core.Run` inside a flow for traced sub-steps:
|
||||
|
||||
```go
|
||||
genkit.DefineFlow(g, "pipeline",
|
||||
func(ctx context.Context, input string) (string, error) {
|
||||
subject, err := core.Run(ctx, "extract-subject", func() (string, error) {
|
||||
return genkit.GenerateText(ctx, g,
|
||||
ai.WithPrompt("Extract the subject from: %s", input),
|
||||
)
|
||||
})
|
||||
if err != nil { return "", err }
|
||||
|
||||
joke, err := core.Run(ctx, "generate-joke", func() (string, error) {
|
||||
return genkit.GenerateText(ctx, g,
|
||||
ai.WithPrompt("Tell me a joke about %s", subject),
|
||||
)
|
||||
})
|
||||
return joke, err
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## HTTP Handlers
|
||||
|
||||
### genkit.Handler
|
||||
|
||||
Convert any flow into an `http.HandlerFunc`:
|
||||
|
||||
```go
|
||||
mux := http.NewServeMux()
|
||||
for _, f := range genkit.ListFlows(g) {
|
||||
mux.HandleFunc("POST /"+f.Name(), genkit.Handler(f))
|
||||
}
|
||||
log.Fatal(server.Start(ctx, "127.0.0.1:8080", mux))
|
||||
```
|
||||
|
||||
### Request/Response Format
|
||||
|
||||
**Non-streaming request:**
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/jokeFlow \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"data": "bananas"}'
|
||||
```
|
||||
|
||||
Response: `{"result": "Why did the banana go to the doctor?..."}`
|
||||
|
||||
**Streaming request:**
|
||||
```bash
|
||||
curl -N -X POST http://localhost:8080/streamingJokeFlow \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"data": "bananas"}'
|
||||
```
|
||||
|
||||
Streaming responses use Server-Sent Events (SSE) format.
|
||||
|
||||
### genkit.HandlerFunc
|
||||
|
||||
For frameworks that expect error-returning handlers:
|
||||
|
||||
```go
|
||||
handler := genkit.HandlerFunc(myFlow)
|
||||
// handler is func(http.ResponseWriter, *http.Request) error
|
||||
```
|
||||
|
||||
### Context Providers
|
||||
|
||||
Inject request context (e.g., auth headers) into flow execution:
|
||||
|
||||
```go
|
||||
mux.HandleFunc("POST /myFlow", genkit.Handler(myFlow,
|
||||
genkit.WithContextProviders(func(ctx context.Context, rd core.RequestData) (api.ActionContext, error) {
|
||||
// rd.Headers contains HTTP headers
|
||||
return api.ActionContext{"userId": rd.Headers.Get("X-User-Id")}, nil
|
||||
}),
|
||||
))
|
||||
```
|
||||
|
||||
### ListFlows
|
||||
|
||||
Get all registered flows for dynamic route setup:
|
||||
|
||||
```go
|
||||
flows := genkit.ListFlows(g) // []api.Action
|
||||
for _, f := range flows {
|
||||
fmt.Println(f.Name())
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,176 @@
|
||||
# Generation
|
||||
|
||||
## GenerateText
|
||||
|
||||
Simplest form. Returns a string.
|
||||
|
||||
```go
|
||||
text, err := genkit.GenerateText(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a joke about %s", topic),
|
||||
)
|
||||
```
|
||||
|
||||
## Generate
|
||||
|
||||
Returns a full `*ModelResponse` with metadata, usage stats, and history.
|
||||
|
||||
```go
|
||||
resp, err := genkit.Generate(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithSystem("You are a helpful assistant."),
|
||||
ai.WithPrompt("Explain %s", topic),
|
||||
)
|
||||
fmt.Println(resp.Text()) // concatenated text
|
||||
fmt.Println(resp.FinishReason) // ai.FinishReasonStop, etc.
|
||||
fmt.Println(resp.Usage) // token counts
|
||||
```
|
||||
|
||||
## GenerateData (Structured Output)
|
||||
|
||||
Returns a typed Go value parsed from the model's JSON output.
|
||||
|
||||
```go
|
||||
type Joke struct {
|
||||
Setup string `json:"setup" jsonschema:"description=The setup of the joke"`
|
||||
Punchline string `json:"punchline" jsonschema:"description=The punchline"`
|
||||
}
|
||||
|
||||
joke, resp, err := genkit.GenerateData[Joke](ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a joke about %s", topic),
|
||||
)
|
||||
// joke is *Joke, resp is *ModelResponse
|
||||
```
|
||||
|
||||
## Streaming
|
||||
|
||||
### GenerateStream
|
||||
|
||||
Returns an iterator. Each value has `.Done`, `.Chunk`, and `.Response`.
|
||||
|
||||
```go
|
||||
stream := genkit.GenerateStream(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a long story about %s", topic),
|
||||
)
|
||||
for result, err := range stream {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Done {
|
||||
finalText := result.Response.Text()
|
||||
break
|
||||
}
|
||||
fmt.Print(result.Chunk.Text()) // incremental text
|
||||
}
|
||||
```
|
||||
|
||||
### GenerateDataStream (Structured Streaming)
|
||||
|
||||
Streams typed partial objects as they arrive.
|
||||
|
||||
```go
|
||||
stream := genkit.GenerateDataStream[Joke](ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a joke about %s", topic),
|
||||
)
|
||||
for result, err := range stream {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.Done {
|
||||
finalJoke := result.Output // *Joke
|
||||
break
|
||||
}
|
||||
partialJoke := result.Chunk // *Joke (partial)
|
||||
}
|
||||
```
|
||||
|
||||
### Callback-Based Streaming
|
||||
|
||||
Use `ai.WithStreaming` with `Generate` for callback-style streaming. The callback receives `*ai.ModelResponseChunk`:
|
||||
|
||||
```go
|
||||
resp, err := genkit.Generate(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Tell me a story"),
|
||||
ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
|
||||
fmt.Print(chunk.Text()) // extract text from chunk
|
||||
return nil
|
||||
}),
|
||||
)
|
||||
// resp contains the final complete response
|
||||
```
|
||||
|
||||
## Common Options
|
||||
|
||||
```go
|
||||
// Model selection
|
||||
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", nil)) // model reference
|
||||
ai.WithModelName("googleai/gemini-flash-latest") // by name string
|
||||
|
||||
// Content
|
||||
ai.WithPrompt("Tell me about %s", topic) // user message (supports fmt verbs)
|
||||
ai.WithSystem("You are a pirate.") // system instructions
|
||||
ai.WithMessages(msg1, msg2) // conversation history
|
||||
ai.WithDocs(doc1, doc2) // context documents
|
||||
ai.WithTextDocs("context 1", "context 2") // context as strings
|
||||
|
||||
// Model config (provider-specific)
|
||||
ai.WithConfig(map[string]any{"temperature": 0.7})
|
||||
```
|
||||
|
||||
## Output Formats
|
||||
|
||||
Control how the model structures its output.
|
||||
|
||||
### By Go Type
|
||||
|
||||
```go
|
||||
// Automatically uses JSON format and instructs model to match the type
|
||||
ai.WithOutputType(MyStruct{})
|
||||
```
|
||||
|
||||
### By Format String
|
||||
|
||||
```go
|
||||
ai.WithOutputFormat(ai.OutputFormatJSON) // single JSON object
|
||||
ai.WithOutputFormat(ai.OutputFormatJSONL) // JSON Lines (one object per line)
|
||||
ai.WithOutputFormat(ai.OutputFormatArray) // JSON array
|
||||
ai.WithOutputFormat(ai.OutputFormatEnum) // constrained enum value
|
||||
ai.WithOutputFormat(ai.OutputFormatText) // plain text (default)
|
||||
```
|
||||
|
||||
### Enum Output
|
||||
|
||||
```go
|
||||
type Color string
|
||||
const (
|
||||
Red Color = "red"
|
||||
Green Color = "green"
|
||||
Blue Color = "blue"
|
||||
)
|
||||
|
||||
text, err := genkit.GenerateText(ctx, g,
|
||||
ai.WithPrompt("What color is the sky?"),
|
||||
ai.WithOutputEnums(Red, Green, Blue),
|
||||
)
|
||||
```
|
||||
|
||||
### Custom Output Instructions
|
||||
|
||||
```go
|
||||
ai.WithOutputInstructions("Return a JSON object with fields: name (string), age (number)")
|
||||
```
|
||||
|
||||
### Combining Format + Schema
|
||||
|
||||
```go
|
||||
// JSONL with a typed schema (useful for streaming lists)
|
||||
genkit.DefinePrompt(g, "characters",
|
||||
ai.WithPrompt("Generate 5 story characters"),
|
||||
ai.WithOutputType([]StoryCharacter{}),
|
||||
ai.WithOutputFormat(ai.OutputFormatJSONL),
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,142 @@
|
||||
# Getting Started
|
||||
|
||||
## Project Setup
|
||||
|
||||
```bash
|
||||
mkdir my-genkit-app && cd my-genkit-app
|
||||
go mod init my-genkit-app
|
||||
go get github.com/genkit-ai/genkit/go@latest
|
||||
```
|
||||
|
||||
Add provider plugin(s) for the models you want to use:
|
||||
```bash
|
||||
go get github.com/genkit-ai/genkit/go/plugins/googlegenai # Google AI / Vertex AI
|
||||
go get github.com/genkit-ai/genkit/go/plugins/anthropic # Anthropic Claude
|
||||
go get github.com/genkit-ai/genkit/go/plugins/compat_oai # OpenAI-compatible
|
||||
go get github.com/genkit-ai/genkit/go/plugins/ollama # Ollama (local)
|
||||
```
|
||||
|
||||
After writing your code, run `go mod tidy` to resolve all dependencies.
|
||||
|
||||
## Initialization
|
||||
|
||||
Every Genkit app starts with `genkit.Init`, which returns a `*Genkit` instance:
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"github.com/genkit-ai/genkit/go/genkit"
|
||||
"github.com/genkit-ai/genkit/go/plugins/googlegenai"
|
||||
)
|
||||
|
||||
ctx := context.Background()
|
||||
g := genkit.Init(ctx,
|
||||
genkit.WithPlugins(&googlegenai.GoogleAI{}),
|
||||
)
|
||||
```
|
||||
|
||||
### The `*Genkit` Instance
|
||||
|
||||
The `*Genkit` value `g` is the central registry. Pass it to every Genkit function:
|
||||
|
||||
```go
|
||||
// Defining resources
|
||||
genkit.DefineFlow(g, "myFlow", ...)
|
||||
genkit.DefineTool(g, "myTool", ...)
|
||||
genkit.DefinePrompt(g, "myPrompt", ...)
|
||||
|
||||
// Generating content
|
||||
genkit.GenerateText(ctx, g, ...)
|
||||
genkit.Generate(ctx, g, ...)
|
||||
```
|
||||
|
||||
Do not store `g` in a global variable. Pass it explicitly through your call chain.
|
||||
|
||||
### Init Options
|
||||
|
||||
```go
|
||||
g := genkit.Init(ctx,
|
||||
// Register one or more plugins
|
||||
genkit.WithPlugins(&googlegenai.GoogleAI{}, &anthropic.Anthropic{}),
|
||||
|
||||
// Set a default model (used when no model is specified)
|
||||
genkit.WithDefaultModel("googleai/gemini-flash-latest"),
|
||||
|
||||
// Set directory for .prompt files (default: "prompts")
|
||||
genkit.WithPromptDir("my-prompts"),
|
||||
|
||||
// Or embed prompts using Go's embed package
|
||||
// genkit.WithPromptFS(promptsFS),
|
||||
)
|
||||
```
|
||||
|
||||
### Embedding Prompts
|
||||
|
||||
Use `go:embed` to bundle `.prompt` files into the binary:
|
||||
|
||||
```go
|
||||
//go:embed prompts
|
||||
var promptsFS embed.FS
|
||||
|
||||
g := genkit.Init(ctx,
|
||||
genkit.WithPlugins(&googlegenai.GoogleAI{}),
|
||||
genkit.WithPromptFS(promptsFS),
|
||||
)
|
||||
```
|
||||
|
||||
## Genkit CLI
|
||||
|
||||
The Genkit CLI provides a local Developer UI for running flows, tracing executions, and inspecting model interactions.
|
||||
|
||||
**Install:**
|
||||
```bash
|
||||
curl -sL cli.genkit.dev | bash
|
||||
```
|
||||
|
||||
**Verify:**
|
||||
```bash
|
||||
genkit --version
|
||||
```
|
||||
|
||||
### Developer UI
|
||||
|
||||
Start your app with the Developer UI attached:
|
||||
|
||||
```bash
|
||||
genkit start -- go run .
|
||||
```
|
||||
|
||||
This launches:
|
||||
- Your app (with tracing enabled)
|
||||
- The Developer UI at `http://localhost:4000`
|
||||
- A telemetry API at `http://localhost:4033`
|
||||
|
||||
Add `-o` to auto-open the UI in your browser:
|
||||
```bash
|
||||
genkit start -o -- go run .
|
||||
```
|
||||
|
||||
The Developer UI lets you:
|
||||
- Run and test flows interactively
|
||||
- View traces for each generation call (inputs, outputs, latency, token usage)
|
||||
- Inspect prompt rendering and tool calls
|
||||
- Debug multi-step flows with per-step trace data
|
||||
|
||||
### Without the CLI
|
||||
|
||||
Set `GENKIT_ENV=dev` to enable the reflection API without the CLI:
|
||||
|
||||
```bash
|
||||
GENKIT_ENV=dev go run .
|
||||
```
|
||||
|
||||
## Import Paths
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/genkit-ai/genkit/go/genkit" // Core: Init, Generate*, DefineFlow, etc.
|
||||
"github.com/genkit-ai/genkit/go/ai" // Types: WithModel, WithPrompt, Message, Part, etc.
|
||||
"github.com/genkit-ai/genkit/go/core" // Low-level: Run (sub-steps), Flow types
|
||||
"github.com/genkit-ai/genkit/go/plugins/server" // server.Start for HTTP
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,256 @@
|
||||
# Prompts
|
||||
|
||||
## DefinePrompt
|
||||
|
||||
Define a reusable prompt in code with a default model and template.
|
||||
|
||||
```go
|
||||
jokePrompt := genkit.DefinePrompt(g, "joke",
|
||||
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", nil)),
|
||||
ai.WithInputType(JokeRequest{Topic: "example"}),
|
||||
ai.WithPrompt("Tell me a joke about {{topic}}."),
|
||||
)
|
||||
```
|
||||
|
||||
### Execute
|
||||
|
||||
```go
|
||||
resp, err := jokePrompt.Execute(ctx,
|
||||
ai.WithInput(map[string]any{"topic": "cats"}),
|
||||
)
|
||||
fmt.Println(resp.Text())
|
||||
```
|
||||
|
||||
### ExecuteStream
|
||||
|
||||
```go
|
||||
stream := jokePrompt.ExecuteStream(ctx,
|
||||
ai.WithInput(map[string]any{"topic": "cats"}),
|
||||
)
|
||||
for result, err := range stream {
|
||||
if err != nil { return err }
|
||||
if result.Done { break }
|
||||
fmt.Print(result.Chunk.Text())
|
||||
}
|
||||
```
|
||||
|
||||
### Override Options at Execution
|
||||
|
||||
```go
|
||||
resp, err := jokePrompt.Execute(ctx,
|
||||
ai.WithInput(map[string]any{"topic": "cats"}),
|
||||
ai.WithModelName("googleai/gemini-pro-latest"), // override model
|
||||
ai.WithConfig(map[string]any{"temperature": 0.9}),
|
||||
ai.WithTools(myTool),
|
||||
)
|
||||
```
|
||||
|
||||
## DefineDataPrompt (Typed Input/Output)
|
||||
|
||||
Strongly-typed prompts with Go generics.
|
||||
|
||||
```go
|
||||
type JokeRequest struct {
|
||||
Topic string `json:"topic"`
|
||||
}
|
||||
|
||||
type Joke struct {
|
||||
Setup string `json:"setup" jsonschema:"description=The setup"`
|
||||
Punchline string `json:"punchline" jsonschema:"description=The punchline"`
|
||||
}
|
||||
|
||||
jokePrompt := genkit.DefineDataPrompt[JokeRequest, *Joke](g, "structured-joke",
|
||||
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", nil)),
|
||||
ai.WithPrompt("Tell me a joke about {{topic}}."),
|
||||
)
|
||||
```
|
||||
|
||||
### Execute (typed)
|
||||
|
||||
```go
|
||||
joke, resp, err := jokePrompt.Execute(ctx, JokeRequest{Topic: "cats"})
|
||||
// joke is *Joke, resp is *ModelResponse
|
||||
```
|
||||
|
||||
### ExecuteStream (typed)
|
||||
|
||||
```go
|
||||
stream := jokePrompt.ExecuteStream(ctx, JokeRequest{Topic: "cats"})
|
||||
for result, err := range stream {
|
||||
if err != nil { return err }
|
||||
if result.Done {
|
||||
finalJoke := result.Output // *Joke
|
||||
break
|
||||
}
|
||||
fmt.Print(result.Chunk) // partial *Joke
|
||||
}
|
||||
```
|
||||
|
||||
## .prompt Files (Dotprompt)
|
||||
|
||||
Define prompts in separate files with YAML frontmatter and Handlebars templates.
|
||||
|
||||
### Basic .prompt File
|
||||
|
||||
`prompts/joke.prompt`:
|
||||
```
|
||||
---
|
||||
model: googleai/gemini-flash-latest
|
||||
input:
|
||||
schema:
|
||||
topic: string
|
||||
---
|
||||
Tell me a joke about {{topic}}.
|
||||
```
|
||||
|
||||
### Load and Use
|
||||
|
||||
```go
|
||||
// LookupPrompt returns Prompt (untyped: map[string]any input, string output)
|
||||
jokePrompt := genkit.LookupPrompt(g, "joke")
|
||||
resp, err := jokePrompt.Execute(ctx,
|
||||
ai.WithInput(map[string]any{"topic": "cats"}),
|
||||
)
|
||||
```
|
||||
|
||||
### Typed .prompt File
|
||||
|
||||
`prompts/structured-joke.prompt`:
|
||||
```
|
||||
---
|
||||
model: googleai/gemini-flash-latest
|
||||
config:
|
||||
thinkingConfig:
|
||||
thinkingBudget: 0
|
||||
input:
|
||||
schema: JokeRequest
|
||||
output:
|
||||
format: json
|
||||
schema: Joke
|
||||
---
|
||||
Tell me a joke about {{topic}}.
|
||||
```
|
||||
|
||||
Register Go types so the .prompt file can reference them by name:
|
||||
```go
|
||||
genkit.DefineSchemaFor[JokeRequest](g)
|
||||
genkit.DefineSchemaFor[Joke](g)
|
||||
|
||||
jokePrompt := genkit.LookupDataPrompt[JokeRequest, *Joke](g, "structured-joke")
|
||||
joke, resp, err := jokePrompt.Execute(ctx, JokeRequest{Topic: "cats"})
|
||||
```
|
||||
|
||||
### LoadPrompt (Explicit Path)
|
||||
|
||||
```go
|
||||
prompt := genkit.LoadPrompt(g, "./prompts/countries.prompt", "countries")
|
||||
resp, err := prompt.Execute(ctx)
|
||||
```
|
||||
|
||||
### .prompt File Features
|
||||
|
||||
**Multi-message prompts with roles:**
|
||||
```
|
||||
---
|
||||
model: googleai/gemini-flash-latest
|
||||
input:
|
||||
schema:
|
||||
question: string
|
||||
---
|
||||
{{ role "system" }}
|
||||
You are a helpful assistant.
|
||||
|
||||
{{ role "user" }}
|
||||
{{question}}
|
||||
```
|
||||
|
||||
**Media in prompts:**
|
||||
```
|
||||
---
|
||||
model: googleai/gemini-flash-latest
|
||||
input:
|
||||
schema:
|
||||
videoUrl: string
|
||||
contentType: string
|
||||
---
|
||||
{{ role "user" }}
|
||||
Summarize this video:
|
||||
{{media url=videoUrl contentType=contentType}}
|
||||
```
|
||||
|
||||
**Conditionals and loops:**
|
||||
```
|
||||
---
|
||||
input:
|
||||
schema:
|
||||
topic: string
|
||||
dietaryRestrictions?(array): string
|
||||
---
|
||||
Write a recipe about {{topic}}.
|
||||
{{#if dietaryRestrictions}}
|
||||
Dietary restrictions: {{#each dietaryRestrictions}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}.
|
||||
{{/if}}
|
||||
```
|
||||
|
||||
**Inline schema in .prompt file:**
|
||||
```
|
||||
---
|
||||
model: googleai/gemini-flash-latest
|
||||
input:
|
||||
schema:
|
||||
topic: string
|
||||
style?: string
|
||||
output:
|
||||
format: json
|
||||
schema:
|
||||
title: string
|
||||
body: string
|
||||
tags(array): string
|
||||
---
|
||||
Write an article about {{topic}}.
|
||||
{{#if style}}Write in a {{style}} style.{{/if}}
|
||||
```
|
||||
|
||||
## Schemas
|
||||
|
||||
### DefineSchemaFor (from Go type)
|
||||
|
||||
Registers a Go struct as a named schema for use in `.prompt` files.
|
||||
|
||||
```go
|
||||
genkit.DefineSchemaFor[JokeRequest](g)
|
||||
genkit.DefineSchemaFor[Joke](g)
|
||||
```
|
||||
|
||||
The schema name matches the Go type name. Use `jsonschema` struct tags for metadata:
|
||||
|
||||
```go
|
||||
type Recipe struct {
|
||||
Title string `json:"title" jsonschema:"description=The recipe title"`
|
||||
Difficulty string `json:"difficulty" jsonschema:"enum=easy,enum=medium,enum=hard"`
|
||||
Ingredients []Ingredient `json:"ingredients"`
|
||||
Steps []string `json:"steps"`
|
||||
}
|
||||
|
||||
type Ingredient struct {
|
||||
Name string `json:"name"`
|
||||
Amount float64 `json:"amount"`
|
||||
Unit string `json:"unit"`
|
||||
}
|
||||
```
|
||||
|
||||
### DefineSchema (manual JSON Schema)
|
||||
|
||||
```go
|
||||
genkit.DefineSchema(g, "Recipe", map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"title": map[string]any{"type": "string"},
|
||||
"ingredients": map[string]any{
|
||||
"type": "array",
|
||||
"items": map[string]any{"type": "object"},
|
||||
},
|
||||
},
|
||||
"required": []string{"title", "ingredients"},
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,157 @@
|
||||
# Model Providers
|
||||
|
||||
## Google AI (Gemini)
|
||||
|
||||
```go
|
||||
import "github.com/genkit-ai/genkit/go/plugins/googlegenai"
|
||||
|
||||
g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{}))
|
||||
```
|
||||
|
||||
**Env var:** `GEMINI_API_KEY` or `GOOGLE_API_KEY`
|
||||
|
||||
Model names follow the format `googleai/<model-id>`. Look up the latest model IDs at https://ai.google.dev/gemini-api/docs/models.
|
||||
|
||||
```go
|
||||
// By name string
|
||||
ai.WithModelName("googleai/gemini-flash-latest")
|
||||
|
||||
// Model ref with provider-specific config
|
||||
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", &genai.GenerateContentConfig{
|
||||
ThinkingConfig: &genai.ThinkingConfig{
|
||||
ThinkingBudget: genai.Ptr[int32](0), // disable thinking
|
||||
},
|
||||
}))
|
||||
|
||||
// Lookup a model instance
|
||||
m := googlegenai.GoogleAIModel(g, "gemini-flash-latest")
|
||||
```
|
||||
|
||||
## Vertex AI
|
||||
|
||||
```go
|
||||
import "github.com/genkit-ai/genkit/go/plugins/googlegenai"
|
||||
|
||||
g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.VertexAI{}))
|
||||
```
|
||||
|
||||
**Env vars:** `GOOGLE_CLOUD_PROJECT`, `GOOGLE_CLOUD_LOCATION` (or `GOOGLE_CLOUD_REGION`)
|
||||
|
||||
Uses Application Default Credentials (`gcloud auth application-default login`).
|
||||
|
||||
Model names follow the format `vertexai/<model-id>`. Same model IDs as Google AI.
|
||||
|
||||
```go
|
||||
ai.WithModelName("vertexai/gemini-flash-latest")
|
||||
```
|
||||
|
||||
## Anthropic (Claude)
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/anthropics/anthropic-sdk-go" // Anthropic SDK types
|
||||
ant "github.com/genkit-ai/genkit/go/plugins/anthropic" // Genkit plugin
|
||||
)
|
||||
|
||||
g := genkit.Init(ctx, genkit.WithPlugins(&ant.Anthropic{}))
|
||||
```
|
||||
|
||||
**Env var:** `ANTHROPIC_API_KEY`
|
||||
|
||||
Model names follow the format `anthropic/<model-id>`. Look up the latest model IDs at https://docs.anthropic.com/en/docs/about-claude/models.
|
||||
|
||||
```go
|
||||
// By name
|
||||
ai.WithModelName("anthropic/claude-sonnet-4-6")
|
||||
|
||||
// With provider-specific config (uses Anthropic SDK types via ai.WithConfig)
|
||||
ai.WithConfig(&anthropic.MessageNewParams{
|
||||
Temperature: anthropic.Float(1.0),
|
||||
MaxTokens: *anthropic.IntPtr(2000),
|
||||
Thinking: anthropic.ThinkingConfigParamUnion{
|
||||
OfEnabled: &anthropic.ThinkingConfigEnabledParam{
|
||||
BudgetTokens: *anthropic.IntPtr(1024),
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## OpenAI-Compatible (compat_oai)
|
||||
|
||||
Works with any OpenAI-compatible API: OpenAI, DeepSeek, xAI, etc.
|
||||
|
||||
```go
|
||||
import "github.com/genkit-ai/genkit/go/plugins/compat_oai"
|
||||
|
||||
openaiPlugin := &compat_oai.OpenAICompatible{
|
||||
Provider: "openai", // unique identifier
|
||||
APIKey: os.Getenv("OPENAI_API_KEY"),
|
||||
// BaseURL: "https://custom-endpoint/v1", // for non-OpenAI providers
|
||||
}
|
||||
g := genkit.Init(ctx, genkit.WithPlugins(openaiPlugin))
|
||||
```
|
||||
|
||||
Define models explicitly (not auto-discovered):
|
||||
|
||||
```go
|
||||
model := openaiPlugin.DefineModel("openai", "gpt-4o", compat_oai.ModelOptions{})
|
||||
```
|
||||
|
||||
Use with:
|
||||
```go
|
||||
ai.WithModel(model)
|
||||
```
|
||||
|
||||
## Ollama (Local Models)
|
||||
|
||||
```go
|
||||
import "github.com/genkit-ai/genkit/go/plugins/ollama"
|
||||
|
||||
ollamaPlugin := &ollama.Ollama{
|
||||
ServerAddress: "http://localhost:11434",
|
||||
Timeout: 60, // seconds
|
||||
}
|
||||
g := genkit.Init(ctx, genkit.WithPlugins(ollamaPlugin))
|
||||
```
|
||||
|
||||
Define models explicitly:
|
||||
|
||||
```go
|
||||
model := ollamaPlugin.DefineModel(g,
|
||||
ollama.ModelDefinition{
|
||||
Name: "llama3.1",
|
||||
Type: "chat", // or "generate"
|
||||
},
|
||||
nil, // optional *ModelOptions
|
||||
)
|
||||
```
|
||||
|
||||
Use with:
|
||||
```go
|
||||
ai.WithModel(model)
|
||||
```
|
||||
|
||||
## Multiple Providers
|
||||
|
||||
Register multiple plugins in a single Genkit instance:
|
||||
|
||||
```go
|
||||
g := genkit.Init(ctx,
|
||||
genkit.WithPlugins(
|
||||
&googlegenai.GoogleAI{},
|
||||
&ant.Anthropic{},
|
||||
),
|
||||
genkit.WithDefaultModel("googleai/gemini-flash-latest"),
|
||||
)
|
||||
|
||||
// Use different models per call
|
||||
text1, _ := genkit.GenerateText(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("Hello from Gemini"),
|
||||
)
|
||||
|
||||
text2, _ := genkit.GenerateText(ctx, g,
|
||||
ai.WithModelName("anthropic/claude-sonnet-4-6"),
|
||||
ai.WithPrompt("Hello from Claude"),
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,178 @@
|
||||
# Tools
|
||||
|
||||
## DefineTool
|
||||
|
||||
Define a tool the model can call during generation.
|
||||
|
||||
```go
|
||||
type WeatherInput struct {
|
||||
Location string `json:"location" jsonschema:"description=City name"`
|
||||
}
|
||||
|
||||
type WeatherOutput struct {
|
||||
Temperature float64 `json:"temperature"`
|
||||
Conditions string `json:"conditions"`
|
||||
}
|
||||
|
||||
weatherTool := genkit.DefineTool(g, "getWeather",
|
||||
"Gets the current weather for a location.",
|
||||
func(ctx *ai.ToolContext, input WeatherInput) (WeatherOutput, error) {
|
||||
// Call your weather API
|
||||
return WeatherOutput{Temperature: 72, Conditions: "sunny"}, nil
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Using Tools in Generation
|
||||
|
||||
Pass tools to `Generate`, `GenerateText`, or prompts:
|
||||
|
||||
```go
|
||||
resp, err := genkit.Generate(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithPrompt("What's the weather in San Francisco?"),
|
||||
ai.WithTools(weatherTool),
|
||||
)
|
||||
// The model calls the tool automatically and incorporates the result
|
||||
fmt.Println(resp.Text())
|
||||
```
|
||||
|
||||
### Tool Choice
|
||||
|
||||
```go
|
||||
ai.WithToolChoice(ai.ToolChoiceAuto) // model decides (default)
|
||||
ai.WithToolChoice(ai.ToolChoiceRequired) // model must use a tool
|
||||
ai.WithToolChoice(ai.ToolChoiceNone) // model cannot use tools
|
||||
```
|
||||
|
||||
### Max Turns
|
||||
|
||||
Limit how many tool-call round trips the model can make:
|
||||
|
||||
```go
|
||||
ai.WithMaxTurns(3) // default is 5
|
||||
```
|
||||
|
||||
## DefineMultipartTool
|
||||
|
||||
Tools that return both structured output and media content:
|
||||
|
||||
```go
|
||||
screenshotTool := genkit.DefineMultipartTool(g, "screenshot",
|
||||
"Takes a screenshot of the current page",
|
||||
func(ctx *ai.ToolContext, input any) (*ai.MultipartToolResponse, error) {
|
||||
return &ai.MultipartToolResponse{
|
||||
Output: map[string]any{"success": true},
|
||||
Content: []*ai.Part{ai.NewMediaPart("image/png", base64Data)},
|
||||
}, nil
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
## Tool Interrupts
|
||||
|
||||
Pause tool execution to request human input before continuing.
|
||||
|
||||
### Interrupting
|
||||
|
||||
```go
|
||||
type TransferInput struct {
|
||||
ToAccount string `json:"toAccount"`
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
type TransferOutput struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Balance float64 `json:"balance"`
|
||||
}
|
||||
|
||||
type TransferInterrupt struct {
|
||||
Reason string `json:"reason"`
|
||||
ToAccount string `json:"toAccount"`
|
||||
Amount float64 `json:"amount"`
|
||||
Balance float64 `json:"balance"`
|
||||
}
|
||||
|
||||
transferTool := genkit.DefineTool(g, "transferMoney",
|
||||
"Transfers money to another account.",
|
||||
func(ctx *ai.ToolContext, input TransferInput) (TransferOutput, error) {
|
||||
if input.Amount > accountBalance {
|
||||
return TransferOutput{}, ai.InterruptWith(ctx, TransferInterrupt{
|
||||
Reason: "insufficient_balance",
|
||||
ToAccount: input.ToAccount,
|
||||
Amount: input.Amount,
|
||||
Balance: accountBalance,
|
||||
})
|
||||
}
|
||||
// Process transfer...
|
||||
return TransferOutput{Status: "success", Balance: newBalance}, nil
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### Handling Interrupts
|
||||
|
||||
```go
|
||||
resp, err := genkit.Generate(ctx, g,
|
||||
ai.WithModelName("googleai/gemini-flash-latest"),
|
||||
ai.WithTools(transferTool),
|
||||
ai.WithPrompt(userRequest),
|
||||
)
|
||||
|
||||
for resp.FinishReason == ai.FinishReasonInterrupted {
|
||||
var restarts, responses []*ai.Part
|
||||
|
||||
for _, interrupt := range resp.Interrupts() {
|
||||
meta, ok := ai.InterruptAs[TransferInterrupt](interrupt)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
switch meta.Reason {
|
||||
case "insufficient_balance":
|
||||
// RestartWith: re-execute the tool with adjusted input
|
||||
part, err := transferTool.RestartWith(interrupt,
|
||||
ai.WithNewInput(TransferInput{
|
||||
ToAccount: meta.ToAccount,
|
||||
Amount: meta.Balance, // transfer what's available
|
||||
}),
|
||||
)
|
||||
if err != nil { return err }
|
||||
restarts = append(restarts, part)
|
||||
|
||||
case "confirm_large":
|
||||
// RespondWith: provide a response directly without re-executing
|
||||
part, err := transferTool.RespondWith(interrupt,
|
||||
TransferOutput{Status: "cancelled", Message: "User declined"},
|
||||
)
|
||||
if err != nil { return err }
|
||||
responses = append(responses, part)
|
||||
}
|
||||
}
|
||||
|
||||
// Continue generation with the resolved interrupts
|
||||
resp, err = genkit.Generate(ctx, g,
|
||||
ai.WithMessages(resp.History()...),
|
||||
ai.WithTools(transferTool),
|
||||
ai.WithToolRestarts(restarts...),
|
||||
ai.WithToolResponses(responses...),
|
||||
)
|
||||
if err != nil { return err }
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Resume State
|
||||
|
||||
Inside a tool function, check if the tool is being resumed from an interrupt:
|
||||
|
||||
```go
|
||||
func(ctx *ai.ToolContext, input TransferInput) (TransferOutput, error) {
|
||||
if ctx.IsResumed() {
|
||||
// This is a resumed call after an interrupt
|
||||
original, ok := ai.OriginalInputAs[TransferInput](ctx)
|
||||
// original contains the input from the first call
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user