cb2d63d06f
provider/openai: - Fix doubled tool call args (argsComplete flag): Ollama sends complete args in the first streaming chunk then repeats them as delta, causing doubled JSON and 400 errors in elfs - Handle fs: prefix (gemma4 uses fs:grep instead of fs.grep) - Add Reasoning field support for Ollama thinking output cmd/gnoma: - Early TTY detection so logger is created with correct destination before any component gets a reference to it (fixes slog WARN bleed into TUI textarea) permission: - Exempt spawn_elfs and agent tools from safety scanner: elf prompt text may legitimately mention .env/.ssh/credentials patterns and should not be blocked tui/app: - /init retry chain: no-tool-calls → spawn_elfs nudge → write nudge (ask for plain text output) → TUI fallback write from streamBuf - looksLikeAgentsMD + extractMarkdownDoc: validate and clean fallback content before writing (reject refusals, strip narrative preambles) - Collapse thinking output to 3 lines; ctrl+o to expand (live stream and committed messages) - Stream-level filter for model pseudo-tool-call blocks: suppresses <<tool_code>>...</tool_code>> and <<function_call>>...<tool_call|> from entering streamBuf across chunk boundaries - sanitizeAssistantText regex covers both block formats - Reset streamFilterClose at every turn start
112 lines
2.9 KiB
Go
112 lines
2.9 KiB
Go
package openai
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"somegit.dev/Owlibou/gnoma/internal/provider"
|
|
"somegit.dev/Owlibou/gnoma/internal/stream"
|
|
|
|
oai "github.com/openai/openai-go"
|
|
"github.com/openai/openai-go/option"
|
|
)
|
|
|
|
const defaultModel = "gpt-4o"
|
|
|
|
// Provider implements provider.Provider for the OpenAI API.
|
|
type Provider struct {
|
|
client *oai.Client
|
|
name string
|
|
model string
|
|
streamOpts []option.RequestOption // injected per-request (e.g. think:false for Ollama)
|
|
}
|
|
|
|
// New creates an OpenAI provider from config.
|
|
func New(cfg provider.ProviderConfig) (provider.Provider, error) {
|
|
return NewWithStreamOptions(cfg, nil)
|
|
}
|
|
|
|
// NewWithStreamOptions creates an OpenAI provider with extra per-request stream options.
|
|
// Use this for Ollama/llama.cpp adapters that need non-standard body fields.
|
|
func NewWithStreamOptions(cfg provider.ProviderConfig, streamOpts []option.RequestOption) (provider.Provider, error) {
|
|
if cfg.APIKey == "" {
|
|
return nil, fmt.Errorf("openai: api key required")
|
|
}
|
|
|
|
opts := []option.RequestOption{
|
|
option.WithAPIKey(cfg.APIKey),
|
|
}
|
|
if cfg.BaseURL != "" {
|
|
opts = append(opts, option.WithBaseURL(cfg.BaseURL))
|
|
}
|
|
|
|
client := oai.NewClient(opts...)
|
|
|
|
model := cfg.Model
|
|
if model == "" {
|
|
model = defaultModel
|
|
}
|
|
|
|
return &Provider{
|
|
client: &client,
|
|
name: "openai",
|
|
model: model,
|
|
streamOpts: streamOpts,
|
|
}, nil
|
|
}
|
|
|
|
// Stream initiates a streaming chat completion request.
|
|
func (p *Provider) Stream(ctx context.Context, req provider.Request) (stream.Stream, error) {
|
|
model := req.Model
|
|
if model == "" {
|
|
model = p.model
|
|
}
|
|
|
|
params := translateRequest(req)
|
|
params.Model = model
|
|
|
|
raw := p.client.Chat.Completions.NewStreaming(ctx, params, p.streamOpts...)
|
|
|
|
return newOpenAIStream(raw), nil
|
|
}
|
|
|
|
// Name returns "openai".
|
|
func (p *Provider) Name() string { return p.name }
|
|
|
|
// DefaultModel returns the configured default model.
|
|
func (p *Provider) DefaultModel() string { return p.model }
|
|
|
|
// Models returns known OpenAI models with capabilities.
|
|
func (p *Provider) Models(_ context.Context) ([]provider.ModelInfo, error) {
|
|
return []provider.ModelInfo{
|
|
{
|
|
ID: "gpt-4o", Name: "GPT-4o", Provider: p.name,
|
|
Capabilities: provider.Capabilities{
|
|
ToolUse: true, JSONOutput: true, Vision: true,
|
|
ContextWindow: 128000, MaxOutput: 16384,
|
|
},
|
|
},
|
|
{
|
|
ID: "gpt-4o-mini", Name: "GPT-4o Mini", Provider: p.name,
|
|
Capabilities: provider.Capabilities{
|
|
ToolUse: true, JSONOutput: true, Vision: true,
|
|
ContextWindow: 128000, MaxOutput: 16384,
|
|
},
|
|
},
|
|
{
|
|
ID: "o3", Name: "o3", Provider: p.name,
|
|
Capabilities: provider.Capabilities{
|
|
ToolUse: true, JSONOutput: true, Thinking: true,
|
|
ContextWindow: 200000, MaxOutput: 100000,
|
|
},
|
|
},
|
|
{
|
|
ID: "o3-mini", Name: "o3 Mini", Provider: p.name,
|
|
Capabilities: provider.Capabilities{
|
|
ToolUse: true, JSONOutput: true, Thinking: true,
|
|
ContextWindow: 200000, MaxOutput: 100000,
|
|
},
|
|
},
|
|
}, nil
|
|
}
|