Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions go/ai/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

// Package ai_test provides examples for ai package helper functions.
//
// The ai package contains helper types and functions used with genkit.
// Most generation and definition functions are in the genkit package;
// see that package for the primary API documentation.
package ai_test

import (
"fmt"

"github.com/firebase/genkit/go/ai"
)

// This example demonstrates creating different types of message parts.
func ExampleNewTextPart() {
// Create a text part
part := ai.NewTextPart("Hello, world!")
fmt.Println(part.Text)
// Output: Hello, world!
}

// This example demonstrates creating a message with text content.
func ExampleNewUserTextMessage() {
// Create a user message with text
msg := ai.NewUserTextMessage("What is the capital of France?")
fmt.Println("Role:", msg.Role)
fmt.Println("Text:", msg.Content[0].Text)
// Output:
// Role: user
// Text: What is the capital of France?
}

// This example demonstrates creating system and model messages.
func ExampleNewSystemTextMessage() {
// Create a system message
sysMsg := ai.NewSystemTextMessage("You are a helpful assistant.")
fmt.Println("System role:", sysMsg.Role)

// Create a model response message
modelMsg := ai.NewModelTextMessage("I'm here to help!")
fmt.Println("Model role:", modelMsg.Role)
// Output:
// System role: system
// Model role: model
}

// This example demonstrates creating a data part for raw string content.
func ExampleNewDataPart() {
// Create a data part with raw string content
part := ai.NewDataPart(`{"name": "Alice", "age": 30}`)
fmt.Println("Is data part:", part.IsData())
fmt.Println("Content:", part.Text)
// Output:
// Is data part: true
// Content: {"name": "Alice", "age": 30}
}

// This example demonstrates accessing text from a Part.
func ExamplePart_Text() {
// Create a part with text
part := ai.NewTextPart("Sample text content")

// Access the text field directly
fmt.Println(part.Text)
// Output: Sample text content
}

// This example demonstrates the Document type used in RAG applications.
func ExampleDocument() {
// Create a document with text content
doc := &ai.Document{
Content: []*ai.Part{
ai.NewTextPart("This is the document content."),
},
Metadata: map[string]any{
"source": "knowledge-base",
"page": 42,
},
}

fmt.Println("Content:", doc.Content[0].Text)
fmt.Println("Source:", doc.Metadata["source"])
// Output:
// Content: This is the document content.
// Source: knowledge-base
}

// This example demonstrates creating an Embedding for vector search.
func ExampleEmbedding() {
// Create an embedding (typically returned by an embedder)
embedding := &ai.Embedding{
Embedding: []float32{0.1, 0.2, 0.3, 0.4, 0.5},
Metadata: map[string]any{
"source": "document-1",
},
}

fmt.Printf("Embedding dimensions: %d\n", len(embedding.Embedding))
fmt.Printf("First value: %.1f\n", embedding.Embedding[0])
// Output:
// Embedding dimensions: 5
// First value: 0.1
}

// This example demonstrates creating a media part for images or other media.
func ExampleNewMediaPart() {
// Create a media part with base64-encoded image data
// In practice, you would encode actual image bytes
imageData := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJ..."
part := ai.NewMediaPart("image/png", imageData)

fmt.Println("Is media:", part.IsMedia())
fmt.Println("Content type:", part.ContentType)
// Output:
// Is media: true
// Content type: image/png
}

// This example demonstrates creating a model reference with configuration.
func ExampleNewModelRef() {
// Create a reference to a model with custom configuration
// The config type depends on the model provider
modelRef := ai.NewModelRef("googleai/gemini-2.5-flash", map[string]any{
"temperature": 0.7,
})

fmt.Println("Model name:", modelRef.Name())
// Output: Model name: googleai/gemini-2.5-flash
}

// This example demonstrates building a multi-turn conversation.
func ExampleNewUserMessage() {
// Build a conversation with multiple parts
userMsg := ai.NewUserMessage(
ai.NewTextPart("What's in this image?"),
ai.NewMediaPart("image/jpeg", "base64data..."),
)

fmt.Println("Role:", userMsg.Role)
fmt.Println("Parts:", len(userMsg.Content))
// Output:
// Role: user
// Parts: 2
}
230 changes: 230 additions & 0 deletions go/core/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

/*
Package core implements Genkit's foundational action system and runtime machinery.

This package is primarily intended for plugin developers and Genkit internals.
Application developers should use the genkit package instead, which provides
a higher-level, more convenient API.

# Actions

Actions are the fundamental building blocks of Genkit. Every operation - flows,
model calls, tool invocations, retrieval - is implemented as an action. Actions
provide:

- Type-safe input/output with JSON schema validation
- Automatic tracing and observability
- Consistent error handling
- Registration in the action registry

Define a non-streaming action:

action := core.DefineAction(registry, "myAction",
func(ctx context.Context, input string) (string, error) {
return "processed: " + input, nil
},
)

result, err := action.Run(context.Background(), "hello")

Define a streaming action that sends chunks during execution:

streamingAction := core.DefineStreamingAction(registry, "countdown",
func(ctx context.Context, start int, cb core.StreamCallback[string]) (string, error) {
for i := start; i > 0; i-- {
if cb != nil {
if err := cb(ctx, fmt.Sprintf("T-%d", i)); err != nil {
return "", err
}
}
time.Sleep(time.Second)
}
return "Liftoff!", nil
},
)

# Flows

Flows are user-defined actions that orchestrate AI operations. They are the
primary way application developers define business logic in Genkit:

flow := core.DefineFlow(registry, "myFlow",
func(ctx context.Context, input string) (string, error) {
// Use Run to create traced sub-steps
result, err := core.Run(ctx, "step1", func() (string, error) {
return process(input), nil
})
if err != nil {
return "", err
}
return result, nil
},
)

Streaming flows can send intermediate results to callers:

streamingFlow := core.DefineStreamingFlow(registry, "generateReport",
func(ctx context.Context, input Input, cb core.StreamCallback[Progress]) (Report, error) {
for i := 0; i < 100; i += 10 {
if cb != nil {
cb(ctx, Progress{Percent: i})
}
// ... work ...
}
return Report{...}, nil
},
)

# Traced Steps with Run

Use [Run] within flows to create traced sub-operations. Each Run call creates
a span in the trace that's visible in the Genkit Developer UI:

result, err := core.Run(ctx, "fetchData", func() (Data, error) {
return fetchFromAPI()
})

processed, err := core.Run(ctx, "processData", func() (Result, error) {
return process(result)
})

# Middleware

Actions support middleware for cross-cutting concerns like logging, metrics,
or authentication:

loggingMiddleware := func(next core.StreamingFunc[string, string, struct{}]) core.StreamingFunc[string, string, struct{}] {
return func(ctx context.Context, input string, cb core.StreamCallback[struct{}]) (string, error) {
log.Printf("Input: %s", input)
output, err := next(ctx, input, cb)
log.Printf("Output: %s, Error: %v", output, err)
return output, err
}
}

Chain multiple middleware together:

combined := core.ChainMiddleware(loggingMiddleware, metricsMiddleware)
wrappedFn := combined(originalFunc)

# Schema Management

Register JSON schemas for use in prompts and validation:

// Define a schema from a map
core.DefineSchema(registry, "Person", map[string]any{
"type": "object",
"properties": map[string]any{
"name": map[string]any{"type": "string"},
"age": map[string]any{"type": "integer"},
},
"required": []any{"name"},
})

// Define a schema from a Go type (recommended)
core.DefineSchemaFor[Person](registry)

Schemas can be referenced in .prompt files by name.

# Plugin Development

Plugins extend Genkit's functionality by providing models, tools, retrievers,
and other capabilities. Implement the [api.Plugin] interface:

type MyPlugin struct {
APIKey string
}

func (p *MyPlugin) Name() string {
return "myplugin"
}

func (p *MyPlugin) Init(ctx context.Context) []api.Action {
// Initialize the plugin and return actions to register
model := ai.DefineModel(...)
tool := ai.DefineTool(...)
return []api.Action{model, tool}
}

For plugins that resolve actions dynamically (e.g., listing available models
from an API), implement [api.DynamicPlugin]:

type DynamicModelPlugin struct{}

func (p *DynamicModelPlugin) ListActions(ctx context.Context) []api.ActionDesc {
// Return descriptors of available actions
return []api.ActionDesc{
{Key: "/model/myplugin/model-a", Name: "model-a"},
{Key: "/model/myplugin/model-b", Name: "model-b"},
}
}

func (p *DynamicModelPlugin) ResolveAction(atype api.ActionType, name string) api.Action {
// Create and return the action on demand
return createModel(name)
}

# Background Actions

For long-running operations, use background actions that return immediately
with an operation ID that can be polled for completion:

bgAction := core.DefineBackgroundAction(registry, "longTask",
func(ctx context.Context, input Input) (Output, error) {
// Start the operation
return startLongOperation(input)
},
func(ctx context.Context, op *core.Operation[Output]) (*core.Operation[Output], error) {
// Check operation status
return checkOperationStatus(op)
},
)

# Error Handling

Return user-facing errors with appropriate status codes:

if err := validate(input); err != nil {
return nil, core.NewPublicError(core.INVALID_ARGUMENT, "Invalid input", map[string]any{
"field": "email",
"error": err.Error(),
})
}

For internal errors that should be logged but not exposed to users:

return nil, core.NewError(core.INTERNAL, "database connection failed: %v", err)

# Context

Access action context for metadata and configuration:

ctx := core.FromContext(ctx)
if ctx != nil {
// Access action-specific context values
}

Set action context for nested operations:

ctx = core.WithActionContext(ctx, core.ActionContext{
"requestId": requestID,
})

For more information, see https://genkit.dev/docs/plugins
*/
package core
Loading
Loading