integration

package
v0.9.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 24, 2026 License: MIT Imports: 0 Imported by: 0

Documentation

Overview

Package integration contains example-style tests that demonstrate how to call the inference-go library against real providers.

The package is internal so it does not become part of the public API surface, but you can run the examples locally with:

go test ./internal/integration -run Example

All examples are best-effort: they only attempt live API calls when the relevant environment variables are set, e.g.:

ANTHROPIC_API_KEY=<your key>
OPENAI_API_KEY=<your key>
Example (Anthropic_basicConversation)

Example_anthropic_basicConversation demonstrates a minimal non-streaming call to Anthropic's Messages API using the normalized inference-go API.

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

ps, err := newProviderSetWithDebug()
if err != nil {
	fmt.Fprintln(os.Stderr, "error creating ProviderSetAPI:", err)
	return
}

_, err = ps.AddProvider(ctx, "anthropic", &inference.AddProviderConfig{
	SDKType:                  spec.ProviderSDKTypeAnthropic,
	Origin:                   spec.DefaultAnthropicOrigin,
	ChatCompletionPathPrefix: spec.DefaultAnthropicChatCompletionPrefix,
	APIKeyHeaderKey:          spec.DefaultAnthropicAuthorizationHeaderKey,
	// DefaultHeaders are optional; the official SDK sets anthropic-version.
})
if err != nil {
	fmt.Fprintln(os.Stderr, "error adding Anthropic provider:", err)
	return
}

apiKey := os.Getenv("ANTHROPIC_API_KEY")
if apiKey == "" {
	fmt.Fprintln(os.Stderr, "ANTHROPIC_API_KEY not set; skipping live Anthropic call")
	fmt.Println("OK")
	return
}
if err := ps.SetProviderAPIKey(ctx, "anthropic", apiKey); err != nil {
	fmt.Fprintln(os.Stderr, "error setting Anthropic API key:", err)
	return
}

req := &spec.FetchCompletionRequest{
	ModelParam: spec.ModelParam{
		Name:            "claude-haiku-4-5-20251001",
		Stream:          false,
		MaxPromptLength: 4096,
		MaxOutputLength: 256,
		SystemPrompt:    "You are a concise, helpful assistant.",
	},
	Inputs: []spec.InputUnion{
		{
			Kind: spec.InputKindInputMessage,
			InputMessage: &spec.InputOutputContent{
				Role: spec.RoleUser,
				Contents: []spec.InputOutputContentItemUnion{
					{
						Kind: spec.ContentItemKindText,
						TextItem: &spec.ContentItemText{
							Text: "Say hello from Anthropic in one short sentence.",
						},
					},
				},
			},
		},
	},
}

resp, err := ps.FetchCompletion(ctx, "anthropic", req, &spec.FetchCompletionOptions{CompletionKey: "haiku45"})
if err != nil {
	fmt.Fprintln(os.Stderr, "FetchCompletion error:", err)
	if resp != nil && resp.Error != nil {
		fmt.Fprintln(os.Stderr, "Provider error:", resp.Error.Message)
	}
	return
}

for _, out := range resp.Outputs {
	if out.Kind != spec.OutputKindOutputMessage || out.OutputMessage == nil {
		continue
	}
	for _, c := range out.OutputMessage.Contents {
		if c.Kind == spec.ContentItemKindText && c.TextItem != nil {
			fmt.Fprintln(os.Stderr, "Anthropic assistant:", c.TextItem.Text)
		}
	}
}
fmt.Println("OK")
Output:
OK
Example (Anthropic_toolsAndThinkingStreaming)

Example_anthropic_toolsAndThinkingStreaming demonstrates:

  • streaming text + thinking
  • function tools + anthropic server web search
  • JSON schema output request
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()

ps, err := newProviderSetWithDebug()
if err != nil {
	fmt.Fprintln(os.Stderr, "error creating ProviderSetAPI:", err)
	return
}

_, err = ps.AddProvider(ctx, "anthropic", &inference.AddProviderConfig{
	SDKType:                  spec.ProviderSDKTypeAnthropic,
	Origin:                   spec.DefaultAnthropicOrigin,
	ChatCompletionPathPrefix: spec.DefaultAnthropicChatCompletionPrefix,
	APIKeyHeaderKey:          spec.DefaultAnthropicAuthorizationHeaderKey,
})
if err != nil {
	fmt.Fprintln(os.Stderr, "error adding Anthropic provider:", err)
	return
}

apiKey := os.Getenv("ANTHROPIC_API_KEY")
if apiKey == "" {
	fmt.Fprintln(os.Stderr, "ANTHROPIC_API_KEY not set; skipping live Anthropic call")
	fmt.Println("OK")
	return
}
if err := ps.SetProviderAPIKey(ctx, "anthropic", apiKey); err != nil {
	fmt.Fprintln(os.Stderr, "error setting Anthropic API key:", err)
	return
}

tools := []spec.ToolChoice{
	{
		Type:        spec.ToolTypeFunction,
		ID:          "extract-key-points",
		Name:        "extract_key_points",
		Description: "Extract 3 key points from the provided text.",
		Arguments: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"text": map[string]any{"type": "string"},
			},
			"required":             []any{"text"},
			"additionalProperties": false,
		},
	},
	{
		Type: spec.ToolTypeWebSearch,
		ID:   "web-search",
		Name: spec.DefaultWebSearchToolName,
		WebSearchArguments: &spec.WebSearchToolChoiceItem{
			MaxUses:           1,
			SearchContextSize: spec.WebSearchContextSizeMedium,
		},
	},
}

req := &spec.FetchCompletionRequest{
	ModelParam: spec.ModelParam{
		Name:         "claude-sonnet-4-6",
		Stream:       true,
		SystemPrompt: "Use tools when helpful. Keep the final answer short.",
		Reasoning: &spec.ReasoningParam{
			Type:  spec.ReasoningTypeSingleWithLevels,
			Level: spec.ReasoningLevelMedium,
		},
		OutputParam: &spec.OutputParam{
			Format: &spec.OutputFormat{
				Kind: spec.OutputFormatKindJSONSchema,
				JSONSchemaParam: &spec.JSONSchemaParam{
					Name: "answer",
					Schema: map[string]any{
						"type": "object",
						"properties": map[string]any{
							"summary":     map[string]any{"type": "string"},
							"source_used": map[string]any{"type": "boolean"},
						},
						"required":             []any{"summary", "source_used"},
						"additionalProperties": false,
					},
				},
			},
		},
	},
	Inputs: []spec.InputUnion{{
		Kind: spec.InputKindInputMessage,
		InputMessage: &spec.InputOutputContent{
			Role: spec.RoleUser,
			Contents: []spec.InputOutputContentItemUnion{{
				Kind:     spec.ContentItemKindText,
				TextItem: &spec.ContentItemText{Text: "What is the latest stable Go version? If unknown, say so."},
			}},
		},
	}},
	ToolChoices: tools,
	ToolPolicy: &spec.ToolPolicy{
		Mode:            spec.ToolPolicyModeAuto,
		DisableParallel: true,
	},
}

_, err = ps.FetchCompletion(ctx, "anthropic", req, &spec.FetchCompletionOptions{
	CompletionKey: "sonnet46",
	StreamHandler: func(ev spec.StreamEvent) error {
		switch ev.Kind {
		case spec.StreamContentKindThinking:
			if ev.Thinking != nil {
				fmt.Fprintf(os.Stderr, "[thinking] %s\n", ev.Thinking.Text)
			}
		case spec.StreamContentKindText:
			if ev.Text != nil {
				fmt.Fprint(os.Stderr, ev.Text.Text)
			}
		}
		return nil
	},
})
if err != nil {
	fmt.Fprintln(os.Stderr, "\nFetchCompletion error:", err)
	return
}

fmt.Println("OK")
Output:
OK
Example (OpenAIChat_basicConversation)

Example_openAIChat_basicConversation demonstrates a minimal non-streaming call to OpenAI's Chat Completions API.

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

ps, err := newProviderSetWithDebug()
if err != nil {
	fmt.Fprintln(os.Stderr, "error creating ProviderSetAPI:", err)
	return
}

_, err = ps.AddProvider(ctx, "openai-chat", &inference.AddProviderConfig{
	SDKType:                  spec.ProviderSDKTypeOpenAIChatCompletions,
	Origin:                   spec.DefaultOpenAIOrigin,
	ChatCompletionPathPrefix: spec.DefaultOpenAIChatCompletionsPrefix,
	APIKeyHeaderKey:          spec.DefaultAuthorizationHeaderKey,
	DefaultHeaders:           spec.OpenAIChatCompletionsDefaultHeaders,
})
if err != nil {
	fmt.Fprintln(os.Stderr, "error adding OpenAI Chat provider:", err)
	return
}

apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
	fmt.Fprintln(os.Stderr, "OPENAI_API_KEY not set; skipping live OpenAI Chat call")
	fmt.Println("OK")
	return
}
if err := ps.SetProviderAPIKey(ctx, "openai-chat", apiKey); err != nil {
	fmt.Fprintln(os.Stderr, "error setting OpenAI API key:", err)
	return
}

req := &spec.FetchCompletionRequest{
	ModelParam: spec.ModelParam{
		Name:            "gpt-4.1-mini",
		Stream:          false,
		MaxPromptLength: 4096,
		MaxOutputLength: 256,
		SystemPrompt:    "You are a concise assistant.",
	},
	Inputs: []spec.InputUnion{
		{
			Kind: spec.InputKindInputMessage,
			InputMessage: &spec.InputOutputContent{
				Role: spec.RoleUser,
				Contents: []spec.InputOutputContentItemUnion{
					{
						Kind: spec.ContentItemKindText,
						TextItem: &spec.ContentItemText{
							Text: "Say hello from OpenAI Chat Completions in one short sentence.",
						},
					},
				},
			},
		},
	},
}

resp, err := ps.FetchCompletion(ctx, "openai-chat", req, &spec.FetchCompletionOptions{CompletionKey: "gpt41"})
if err != nil {
	fmt.Fprintln(os.Stderr, "FetchCompletion error:", err)
	if resp != nil && resp.Error != nil {
		fmt.Fprintln(os.Stderr, "Provider error:", resp.Error.Message)
	}
	return
}

for _, out := range resp.Outputs {
	if out.Kind != spec.OutputKindOutputMessage || out.OutputMessage == nil {
		continue
	}
	for _, c := range out.OutputMessage.Contents {
		if c.Kind == spec.ContentItemKindText && c.TextItem != nil {
			fmt.Fprintln(os.Stderr, "OpenAI Chat assistant:", c.TextItem.Text)
		}
	}
}

fmt.Println("OK")
Output:
OK
Example (OpenAIChat_toolsAndJSONSchema)

Example_openAIChat_toolsAndJSONSchema demonstrates:

  • streaming text
  • JSON schema output (response_format=json_schema)
  • function tools
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()

ps, err := newProviderSetWithDebug()
if err != nil {
	fmt.Fprintln(os.Stderr, "error creating ProviderSetAPI:", err)
	return
}

_, err = ps.AddProvider(ctx, "openai-chat", &inference.AddProviderConfig{
	SDKType:                  spec.ProviderSDKTypeOpenAIChatCompletions,
	Origin:                   spec.DefaultOpenAIOrigin,
	ChatCompletionPathPrefix: spec.DefaultOpenAIChatCompletionsPrefix,
	APIKeyHeaderKey:          spec.DefaultAuthorizationHeaderKey,
	DefaultHeaders:           spec.OpenAIChatCompletionsDefaultHeaders,
})
if err != nil {
	fmt.Fprintln(os.Stderr, "error adding OpenAI Chat provider:", err)
	return
}

apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
	fmt.Fprintln(os.Stderr, "OPENAI_API_KEY not set; skipping live OpenAI Chat call")
	fmt.Println("OK")
	return
}
if err := ps.SetProviderAPIKey(ctx, "openai-chat", apiKey); err != nil {
	fmt.Fprintln(os.Stderr, "error setting OpenAI API key:", err)
	return
}

tools := []spec.ToolChoice{
	{
		Type:        spec.ToolTypeFunction,
		ID:          "math",
		Name:        "multiply",
		Description: "Multiply two integers.",
		Arguments: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"a": map[string]any{"type": "integer"},
				"b": map[string]any{"type": "integer"},
			},
			"required":             []any{"a", "b"},
			"additionalProperties": false,
		},
	},
}

req := &spec.FetchCompletionRequest{
	ModelParam: spec.ModelParam{
		Name:         "gpt-4.1",
		Stream:       true,
		SystemPrompt: "answer directly",
		OutputParam: &spec.OutputParam{
			Format: &spec.OutputFormat{
				Kind: spec.OutputFormatKindJSONSchema,
				JSONSchemaParam: &spec.JSONSchemaParam{
					Name: "result",
					Schema: map[string]any{
						"type": "object",
						"properties": map[string]any{
							"answer": map[string]any{"type": "string"},
						},
						"required":             []any{"answer"},
						"additionalProperties": false,
					},
					Strict: true,
				},
			},
		},
	},
	Inputs: []spec.InputUnion{{
		Kind: spec.InputKindInputMessage,
		InputMessage: &spec.InputOutputContent{
			Role: spec.RoleUser,
			Contents: []spec.InputOutputContentItemUnion{{
				Kind: spec.ContentItemKindText,
				TextItem: &spec.ContentItemText{
					Text: "What is 6*7 and what's a recent Go release? (If unknown, say unknown.)",
				},
			}},
		},
	}},
	ToolChoices: tools,
	ToolPolicy: &spec.ToolPolicy{
		Mode:            spec.ToolPolicyModeAuto,
		DisableParallel: true,
	},
}

_, err = ps.FetchCompletion(ctx, "openai-chat", req, &spec.FetchCompletionOptions{
	CompletionKey: "gpt41",
	StreamHandler: func(ev spec.StreamEvent) error {
		if ev.Kind == spec.StreamContentKindText && ev.Text != nil {
			fmt.Fprint(os.Stderr, ev.Text.Text)
		}
		return nil
	},
})
if err != nil {
	fmt.Fprintln(os.Stderr, "\nFetchCompletion error:", err)
	return
}

fmt.Println("OK")
Output:
OK
Example (OpenAIResponses_basicConversation)

Example_openAIResponses_basicConversation demonstrates a minimal non-streaming call to OpenAI's Responses API using text-only input.

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

ps, err := newProviderSetWithDebug()
if err != nil {
	fmt.Fprintln(os.Stderr, "error creating ProviderSetAPI:", err)
	return
}

_, err = ps.AddProvider(ctx, "openai-responses", &inference.AddProviderConfig{
	SDKType: spec.ProviderSDKTypeOpenAIResponses,
	Origin:  spec.DefaultOpenAIOrigin,
	// Only used when Origin is overridden; kept here for clarity.
	ChatCompletionPathPrefix: "/v1/responses",
	APIKeyHeaderKey:          spec.DefaultAuthorizationHeaderKey,
})
if err != nil {
	fmt.Fprintln(os.Stderr, "error adding OpenAI Responses provider:", err)
	return
}

apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
	fmt.Fprintln(os.Stderr, "OPENAI_API_KEY not set; skipping live OpenAI Responses call")
	fmt.Println("OK")
	return
}
if err := ps.SetProviderAPIKey(ctx, "openai-responses", apiKey); err != nil {
	fmt.Fprintln(os.Stderr, "error setting OpenAI API key:", err)
	return
}

req := &spec.FetchCompletionRequest{
	ModelParam: spec.ModelParam{
		Name:            "gpt-5-mini",
		Stream:          false,
		MaxPromptLength: 4096,
		MaxOutputLength: 256,
		SystemPrompt:    "You are a concise assistant.",
		Reasoning: &spec.ReasoningParam{
			Type:  spec.ReasoningTypeSingleWithLevels,
			Level: spec.ReasoningLevelLow,
		},
	},
	Inputs: []spec.InputUnion{
		{
			Kind: spec.InputKindInputMessage,
			InputMessage: &spec.InputOutputContent{
				Role: spec.RoleUser,
				Contents: []spec.InputOutputContentItemUnion{
					{
						Kind: spec.ContentItemKindText,
						TextItem: &spec.ContentItemText{
							Text: "Explain the difference between goroutines and OS threads in 2–3 sentences.",
						},
					},
				},
			},
		},
	},
}

resp, err := ps.FetchCompletion(
	ctx,
	"openai-responses",
	req,
	&spec.FetchCompletionOptions{CompletionKey: "gpt5mini"},
)
if err != nil {
	fmt.Fprintln(os.Stderr, "FetchCompletion error:", err)
	if resp != nil && resp.Error != nil {
		fmt.Fprintln(os.Stderr, "Provider error:", resp.Error.Message)
	}
	return
}

for _, out := range resp.Outputs {
	if out.Kind != spec.OutputKindOutputMessage || out.OutputMessage == nil {
		continue
	}
	for _, c := range out.OutputMessage.Contents {
		if c.Kind == spec.ContentItemKindText && c.TextItem != nil {
			fmt.Fprintln(os.Stderr, "OpenAI Responses assistant:", c.TextItem.Text)
		}
	}
}

fmt.Println("OK")
Output:
OK
Example (OpenAIResponses_toolsAndAttachments)

Example_openAIResponses_toolsAndAttachments demonstrates a more advanced Responses call that:

  • defines function and web-search tools,
  • sends text + image + file as input content,
  • enables streaming of both text and reasoning.

The example only attempts a live call when OPENAI_API_KEY is set. The image/file payloads are placeholders; for a real run, replace them with valid data or URLs.

package main

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/flexigpt/inference-go"
	"github.com/flexigpt/inference-go/spec"
)

const sendFile = true

// Example_openAIResponses_toolsAndAttachments demonstrates a more advanced
// Responses call that:
//
//   - defines function and web-search tools,
//   - sends text + image + file as input content,
//   - enables streaming of both text and reasoning.
//
// The example only attempts a live call when OPENAI_API_KEY is set. The
// image/file payloads are placeholders; for a real run, replace them with
// valid data or URLs.
func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
	defer cancel()

	ps, err := newProviderSetWithDebug()
	if err != nil {
		fmt.Fprintln(os.Stderr, "error creating ProviderSetAPI:", err)
		return
	}

	_, err = ps.AddProvider(ctx, "openai-responses-extended", &inference.AddProviderConfig{
		SDKType:                  spec.ProviderSDKTypeOpenAIResponses,
		Origin:                   spec.DefaultOpenAIOrigin,
		ChatCompletionPathPrefix: "/v1/responses",
		APIKeyHeaderKey:          spec.DefaultAuthorizationHeaderKey,
	})
	if err != nil {
		fmt.Fprintln(os.Stderr, "error adding OpenAI Responses provider:", err)
		return
	}

	apiKey := os.Getenv("OPENAI_API_KEY")
	if apiKey == "" {
		fmt.Fprintln(os.Stderr, "OPENAI_API_KEY not set; skipping extended OpenAI Responses example")
		fmt.Println("OK")
		return
	}
	if err := ps.SetProviderAPIKey(ctx, "openai-responses-extended", apiKey); err != nil {
		fmt.Fprintln(os.Stderr, "error setting OpenAI API key:", err)
		return
	}

	// Tool: summarize_document(document: string, focus: string).
	summarizeTool := spec.ToolChoice{
		Type:        spec.ToolTypeFunction,
		ID:          "summarize-document",
		Name:        "summarize_document",
		Description: "Summarize a document with an optional focus.",
		Arguments: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"document": map[string]any{
					"type":        "string",
					"description": "Full text of the document to summarize.",
				},
				"focus": map[string]any{
					"type":        "string",
					"description": "Optional topic to focus on.",
				},
			},
			"required":             []any{"document"},
			"additionalProperties": false,
		},
	}

	// Web search tool: used for retrieving fresh information when needed.
	webSearchTool := spec.ToolChoice{
		Type:        spec.ToolTypeWebSearch,
		ID:          "web-search",
		Name:        "web_search",
		Description: "Search the web for recent information.",
		WebSearchArguments: &spec.WebSearchToolChoiceItem{
			MaxUses:           2,
			SearchContextSize: spec.WebSearchContextSizeMedium,
			AllowedDomains:    []string{}, // any domain
			UserLocation: &spec.WebSearchToolChoiceItemUserLocation{
				City:     "San Francisco",
				Country:  "US",
				Region:   "CA",
				Timezone: "America/Los_Angeles",
			},
		},
	}

	toolChoices := []spec.ToolChoice{summarizeTool, webSearchTool}

	// Placeholder image data (not a real image). In a real application, provide a valid base64-encoded image.
	// 1x1 transparent PNG.
	fakeImageData := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="

	userMessage := spec.InputOutputContent{
		Role: spec.RoleUser,
		Contents: []spec.InputOutputContentItemUnion{
			{
				Kind: spec.ContentItemKindText,
				TextItem: &spec.ContentItemText{
					Text: "This is a test. Very briefly reply with attached PDF name and data and describe the image. Use tools where appropriate. Keep the final answer short.",
				},
			},
			{
				Kind: spec.ContentItemKindImage,
				ImageItem: &spec.ContentItemImage{
					ImageMIME: spec.DefaultImageDataMIME,
					ImageData: fakeImageData,
					ImageName: "example-image",
					Detail:    spec.ImageDetailLow,
					ImageURL:  "",
					ID:        "abc",
				},
			},
		},
	}
	if sendFile {
		// Placeholder PDF URL.
		fileURL := "https://www.w3schools.com/asp/text/textfile.txt"
		userMessage.Contents = append(userMessage.Contents, spec.InputOutputContentItemUnion{
			Kind: spec.ContentItemKindFile,
			FileItem: &spec.ContentItemFile{
				FileName: "dummy",
				FileMIME: "text/plain",
				FileURL:  fileURL,
			},
		})
	}
	req := &spec.FetchCompletionRequest{
		ModelParam: spec.ModelParam{
			Name:            "gpt-5-mini",
			Stream:          true,
			MaxPromptLength: 8192,
			MaxOutputLength: 8192,
			SystemPrompt: "You are a research assistant that first uses tools " +
				"when needed, then answers succinctly.",
			Reasoning: &spec.ReasoningParam{
				Type:  spec.ReasoningTypeSingleWithLevels,
				Level: spec.ReasoningLevelMedium,
			},
			OutputParam: &spec.OutputParam{
				// Responses maps this to params.Text.format + params.Text.verbosity.
				Verbosity: func() *spec.OutputVerbosity {
					v := spec.OutputVerbosityMedium
					return &v
				}(),
				Format: &spec.OutputFormat{
					Kind: spec.OutputFormatKindJSONSchema,
					JSONSchemaParam: &spec.JSONSchemaParam{
						Name: "final_answer",
						Schema: map[string]any{
							"type": "object",
							"properties": map[string]any{
								"image_description": map[string]any{"type": "string"},
								"file_name":         map[string]any{"type": "string"},
								"answer":            map[string]any{"type": "string"},
							},
							"required":             []any{"image_description", "answer", "file_name"},
							"additionalProperties": false,
						},
						Strict: true,
					},
				},
			},
		},
		Inputs: []spec.InputUnion{
			{
				Kind:         spec.InputKindInputMessage,
				InputMessage: &userMessage,
			},
		},
		ToolChoices: toolChoices,
		ToolPolicy: &spec.ToolPolicy{
			// Demonstrates policy plumbing.
			Mode:            spec.ToolPolicyModeAuto,
			DisableParallel: true,
		},
	}

	// Stream both text and reasoning to stdout.
	opts := &spec.FetchCompletionOptions{
		StreamHandler: func(ev spec.StreamEvent) error {
			switch ev.Kind {
			case spec.StreamContentKindText:
				if ev.Text != nil {
					fmt.Fprintln(os.Stderr, ev.Text.Text)
				}
			case spec.StreamContentKindThinking:
				if ev.Thinking != nil {
					// In a real app you might log this separately; here we
					// just prefix it.
					fmt.Fprintf(os.Stderr, "\n[thinking] %s \n", ev.Thinking.Text)
				}
			}
			return nil
		},
		StreamConfig: &spec.StreamConfig{
			// Use library defaults; override here if you want.
		},
		CompletionKey: "gpt5mini",
	}

	resp, err := ps.FetchCompletion(ctx, "openai-responses-extended", req, opts)
	if err != nil {
		fmt.Fprintln(os.Stderr, "\nFetchCompletion error:", err)
		if resp != nil && resp.Error != nil {
			fmt.Fprintln(os.Stderr, "Provider error:", resp.Error.Message)
		}
		return
	}

	fmt.Fprintln(os.Stderr, "\n\n--- normalized outputs ---")
	for _, out := range resp.Outputs {
		switch out.Kind {
		case spec.OutputKindFunctionToolCall:
			if out.FunctionToolCall != nil {
				fmt.Fprintf(os.Stderr, "Function tool call: %s(%s)\n",
					out.FunctionToolCall.Name,
					out.FunctionToolCall.Arguments,
				)
			}
		case spec.OutputKindWebSearchToolCall:
			if out.WebSearchToolCall != nil {
				fmt.Fprintf(os.Stderr, "Web search call: %+v\n", out.WebSearchToolCall.WebSearchToolCallItems)
			}
		case spec.OutputKindOutputMessage:
			if out.OutputMessage != nil {
				for _, c := range out.OutputMessage.Contents {
					if c.Kind == spec.ContentItemKindText && c.TextItem != nil {
						fmt.Fprintln(os.Stderr, "Final answer:", c.TextItem.Text)
					}
				}
			}
		case spec.OutputKindReasoningMessage:
			if out.ReasoningMessage != nil && len(out.ReasoningMessage.Summary) > 0 {
				fmt.Fprintln(os.Stderr, "Reasoning summary:", out.ReasoningMessage.Summary[0])
			}
		default:
		}
	}

	fmt.Println("OK")
}
Output:
OK

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL