Commit Graph

78 Commits

Author SHA1 Message Date
steve 012c11b775 feat(v2/registry): register ollama-cloud as a first-class provider
Ollama Cloud now appears in Providers() alongside local Ollama, with its
own EnvKey (OLLAMA_API_KEY) and a curated cloud-model list for picker UIs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 18:30:40 +00:00
steve 74b3c53f36 feat(v2): add OllamaCloud() constructor for Ollama Cloud
Ollama Cloud (https://ollama.com) requires a Bearer-token API key and
exposes the same /api/chat surface as local Ollama. OllamaCloud(apiKey)
returns a client preconfigured for the cloud endpoint; Ollama() remains
the local-instance constructor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 18:30:20 +00:00
steve a3e9982d49 refactor(v2/ollama): drop openaicompat shim, use native provider
The Ollama provider now targets /api/chat directly via the native provider
introduced in the previous commits. Public API is unchanged for callers
that go through llm.Ollama() (and is extended by Task 5's OllamaCloud()
constructor).

DefaultBaseURL was renamed to DefaultLocalBaseURL (without the trailing
/v1 segment used by the OpenAI-compat path). registry.go is updated
correspondingly; no other callers referenced the old name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 18:29:59 +00:00
steve f70c7c0842 feat(v2/ollama): implement native Stream() with NDJSON parsing
Reads Ollama's NDJSON stream (one JSON object per line) and emits
provider.StreamEvent values for text, thinking, tool-call start/delta/end,
and a final Done event carrying assembled Response and Usage. Uses
bufio.Scanner with a 4 MiB max-line buffer so multi-KB tool-call deltas
parse cleanly, and accepts tool-call arguments delivered either as
escaped string fragments (delta-style) or a complete JSON object
(one-shot).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 18:29:04 +00:00
steve 583f8724b2 feat(v2/ollama): implement native Complete() with tools, vision, thinking
Non-streaming /api/chat support including:
- Vision via images: []base64
- Tool calls on assistant + tool-role response messages
- think field accepting string reasoning levels (or "true"/"false")
- Authorization header when apiKey is non-empty (cloud mode)

Tool-call arguments are passed as JSON objects to the wire and surfaced
as JSON-string Arguments on provider.ToolCall. Tool calls are assigned
synthetic IDs (tc_<index>) when Ollama omits one, so the round-trip
back as an assistant tool_calls + tool-role message remains correlated.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 18:24:02 +00:00
steve 0e358148eb feat(v2/ollama): scaffold native /api/chat provider
Adds wire types and a Provider struct that will replace the
openaicompat-based Ollama shim with a native /api/chat implementation.
Complete and Stream methods are stubs; subsequent commits implement them.

Adjusts the existing ollama.go to drop the type alias on
openaicompat.Provider (renaming the legacy shim to a temporary internal
helper) so the new native Provider type does not collide. Public New()
still returns the openaicompat-backed provider until Task 4 swaps it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-01 18:22:11 +00:00
steve 5c5d861915 fix(v2): coerce string-encoded numbers/bools in tool arguments
CI / Root Module (push) Failing after 30s
CI / Lint (push) Failing after 3s
CI / V2 Module (push) Successful in 1m54s
LLMs occasionally return numeric or boolean tool-call fields as JSON
strings (e.g. "3" instead of 3, "true" instead of true), which Go's
strict json.Unmarshal rejects. The strict unmarshal stays as the happy
path; on failure we retry with a coercion pass that walks the target
struct (recursing into nested structs, slices, maps, and pointer fields)
and converts strings to the appropriate kind. Returns the original error
if coercion can't recover.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 22:12:56 +00:00
steve cbaf41f50c feat(v2): add ReasoningLevel option; thinking/reasoning across providers
CI / Root Module (push) Failing after 1m30s
CI / Lint (push) Failing after 1m1s
CI / V2 Module (push) Successful in 3m41s
Introduces an opt-in level-based reasoning toggle (low/medium/high) that
each provider translates to its native parameter:

- Anthropic: thinking.budget_tokens (1024/8000/24000), with temperature
  forced to default and MaxTokens auto-grown above the budget.
- OpenAI/xAI/Groq via openaicompat: reasoning_effort string, gated by a
  new Rules.SupportsReasoning predicate so non-reasoning models don't
  receive the parameter. xAI uses Rules.MapReasoningEffort to remap
  "medium" to "high" since its API only accepts low|high.
- Google: thinking_config.thinking_budget + include_thoughts:true.
- DeepSeek: SupportsReasoning=false (reasoner is always-on; the
  reasoning_content trace was already extracted via openaicompat).

Reasoning content is surfaced as Response.Thinking on Complete and as
StreamEventThinking deltas during streaming. Provider-side: extracted
from Anthropic thinking content blocks, Google's part.Thought=true
parts, and the non-standard reasoning_content field that DeepSeek and
Groq emit (parsed out of raw JSON since openai-go doesn't type it).

Public API:
  - llm.ReasoningLevel + ReasoningLow/Medium/High constants
  - llm.WithReasoning(level) request option
  - Model.WithReasoning(level) for baked-in defaults
  - provider.Request.Reasoning, provider.Response.Thinking
  - provider.StreamEventThinking

Tests cover Rules-based gating, MapReasoningEffort, reasoning_content
extraction (Complete + Stream), Anthropic budget mapping, and
temperature suppression when thinking is enabled. Existing behavior is
unchanged when Reasoning is the empty string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 03:58:42 +00:00
steve 34119e5a00 feat: add DeepSeek, Moonshot, xAI, Groq, Ollama; drop v1; migrate TUI to v2
CI / Root Module (push) Failing after 30s
CI / Lint (push) Failing after 50s
CI / V2 Module (push) Successful in 2m14s
Five OpenAI-compatible providers join the library as first-class constructors
(llm.DeepSeek, llm.Moonshot, llm.XAI, llm.Groq, llm.Ollama). Their wire-level
implementation is shared via a new v2/openaicompat package which is the
extracted guts of the old v2/openai provider; each provider supplies its own
Rules value to declare per-model constraints (e.g., DeepSeek Reasoner rejects
tools and temperature, Moonshot/xAI accept images only on *-vision* models,
Groq rejects audio input). v2/openai itself becomes a thin wrapper that sets
RestrictTemperature for o-series and gpt-5 models.

A new provider registry (v2/registry.go) exposes llm.Providers() and drives
the TUI's provider picker so adding a provider in future is a single-file
change.

The TUI at cmd/llm was migrated from v1 to v2 and moved to v2/cmd/llm. With
nothing else depending on v1, the v1 code at the repo root (all .go files,
schema/, internal/, provider/, root go.mod/go.sum) is deleted.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 13:34:39 +00:00
steve 9b91b2f794 test(v2): end-to-end cache-hint propagation through Chat.Send
CI / Lint (push) Successful in 9m35s
CI / Root Module (push) Successful in 10m54s
CI / V2 Module (push) Successful in 11m15s
Verifies that WithPromptCaching() on a Chat results in CacheHints being
set on the provider.Request that reaches the provider layer, and that
omitting the option leaves CacheHints nil (no behavior change for
existing callers).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:40:39 +00:00
steve 34b2e29019 feat(v2/anthropic): apply cache_control markers from CacheHints
buildRequest now tracks a source-index → built-message-index mapping
during the role-merge pass, then uses the mapping to attach
cache_control: {type: ephemeral} markers at the positions indicated by
Request.CacheHints. The last tool, the last system part, and the last
non-system message each get a marker when the corresponding hint is set.

Covers the merge-induced index drift that would otherwise cause the
breakpoint to land on the wrong content block when consecutive same-role
source messages are combined into a single Anthropic message with
multiple content blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:33:25 +00:00
steve 4c6dfb9058 test(v2/anthropic): drop placeholder import sentinel from cache_test.go
Removes the blank-assign workaround that was only needed because the
anth import was being kept alive for Task 5's use. Task 5 will bring
the import back when it actually references anth.CacheControlTypeEphemeral.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:29:14 +00:00
steve a6b5544674 refactor(v2/anthropic): use MultiSystem for system prompts
Switches buildRequest to emit anthReq.MultiSystem instead of anthReq.System
whenever a system message is present. Upstream's MarshalJSON prefers
MultiSystem when non-empty, so the wire format is unchanged for requests
without cache_control. This refactor is a prerequisite for attaching
cache_control markers to system parts in the next commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:26:55 +00:00
steve 01b18dcf32 test(v2): cover empty-messages and disabled-but-non-nil cacheConfig edges
Adds two boundary tests suggested by code review:
- TestBuildProviderRequest_CachingEnabled_EmptyMessages: verifies
  that caching with an empty message list still emits a CacheHints
  with LastCacheableMessageIndex=-1, not a spurious breakpoint.
- TestBuildProviderRequest_CachingNonNilButDisabled: verifies that
  an explicitly-disabled cacheConfig (non-nil, enabled=false)
  produces nil CacheHints, exercising the &&-guard branch that
  the previous "disabled" test left untested.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:24:55 +00:00
steve 4b401fcc0d feat(v2): populate CacheHints on provider.Request when caching enabled
CI / Lint (push) Successful in 9m36s
CI / Root Module (push) Successful in 10m55s
CI / V2 Module (push) Successful in 11m14s
buildProviderRequest now computes cache-breakpoint positions automatically
when the WithPromptCaching() option is set. It places up to 3 hints:
tools, system, and the index of the last non-system message. Providers
that don't support caching (OpenAI, Google) ignore the field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:22:00 +00:00
steve c4fe0026a2 feat(v2): add WithPromptCaching() request option
CI / Lint (push) Failing after 2m2s
CI / V2 Module (push) Failing after 2m3s
CI / Root Module (push) Has been cancelled
Introduces an opt-in RequestOption that callers can pass to enable
automatic prompt-caching markers. The option populates a cacheConfig
on requestConfig but has no effect yet — plumbing through to
provider.Request and on to the Anthropic provider lands in subsequent
commits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:17:55 +00:00
steve b4bf73136a feat(v2/provider): add CacheHints to Request for prompt caching
Adds an optional CacheHints field on provider.Request that carries
cache-breakpoint placement directives from the public llm package down
to individual provider implementations. Anthropic will consume these in
a follow-up commit; OpenAI and Google ignore them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-09 19:14:44 +00:00
Claude bcb91e14b0 Merge pull request 'feat: comprehensive token usage tracking for V2' (#3) from feature/comprehensive-token-usage-tracking into main
CI / Lint (push) Successful in 9m38s
CI / V2 Module (push) Successful in 11m6s
CI / Root Module (push) Successful in 11m53s
2026-03-02 04:35:42 +00:00
steve 5b687839b2 feat: comprehensive token usage tracking for V2
CI / Lint (pull_request) Successful in 10m18s
CI / Root Module (pull_request) Successful in 11m4s
CI / V2 Module (pull_request) Successful in 11m5s
Add provider-specific usage details, fix streaming usage, and return
usage from all high-level APIs (Chat.Send, Generate[T], Agent.Run).

Breaking changes:
- Chat.Send/SendMessage/SendWithImages now return (string, *Usage, error)
- Generate[T]/GenerateWith[T] now return (T, *Usage, error)
- Agent.Run/RunMessages now return (string, *Usage, error)

New features:
- Usage.Details map for provider-specific token breakdowns
  (reasoning, cached, audio, thoughts tokens)
- OpenAI streaming now captures usage via StreamOptions.IncludeUsage
- Google streaming now captures UsageMetadata from final chunk
- UsageTracker.Details() for accumulated detail totals
- ModelPricing and PricingRegistry for cost computation

Closes #2

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 04:33:18 +00:00
steve 7e1705c385 feat: add audio input support to v2 providers
CI / Lint (push) Successful in 9m37s
CI / Root Module (push) Successful in 10m53s
CI / V2 Module (push) Successful in 11m9s
Add Audio struct alongside Image for sending audio attachments to
multimodal LLMs. OpenAI uses input_audio content parts (wav/mp3),
Google Gemini uses genai.NewPartFromBytes, and Anthropic skips
audio gracefully since it's not supported.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 21:00:56 -05:00
steve fc2218b5fe Add comprehensive test suite for sandbox package (78 tests)
CI / Lint (push) Successful in 9m35s
CI / V2 Module (push) Successful in 10m39s
CI / Root Module (push) Successful in 11m2s
Expanded from 22 basic tests to 78 tests covering error injection,
task polling, IP discovery, context cancellation, HTTP error codes,
concurrent access, SSH lifecycle, and request verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 01:10:59 -05:00
steve 23c9068022 Add sandbox package for isolated Linux containers via Proxmox LXC
CI / V2 Module (push) Successful in 11m46s
CI / Root Module (push) Successful in 11m50s
CI / Lint (push) Successful in 9m28s
Provides a complete lifecycle manager for ephemeral sandbox environments:
- ProxmoxClient: thin REST wrapper for container CRUD, IP discovery, internet toggle
- SSHExecutor: persistent SSH/SFTP for command execution and file transfer
- Manager/Sandbox: high-level orchestrator tying Proxmox + SSH together
- 22 unit tests with mock Proxmox HTTP server
- Proxmox setup & hardening guide (docs/sandbox-setup.md)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 00:47:45 -05:00
steve 87ec56a2be Add agent sub-package for composable LLM agents
CI / Lint (push) Successful in 9m46s
CI / V2 Module (push) Successful in 12m5s
CI / Root Module (push) Successful in 12m6s
Introduces v2/agent with a minimal API: Agent, New(), Run(), and AsTool().
Agents wrap a model + system prompt + tools. AsTool() turns an agent into
a llm.Tool, enabling parent agents to delegate to sub-agents through the
normal tool-call loop — no channels, pools, or orchestration needed.

Also exports NewClient(provider.Provider) for custom provider integration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 23:17:19 -05:00
steve be572a76f4 Add structured output support with Generate[T] and GenerateWith[T]
CI / Lint (push) Successful in 9m35s
CI / V2 Module (push) Successful in 11m43s
CI / Root Module (push) Successful in 11m53s
Generic functions that use the "hidden tool" technique to force models
to return structured JSON matching a Go struct's schema, replacing the
verbose "tool as structured output" pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 22:36:33 -05:00
steve 6a7eeef619 Add comprehensive test suite for v2 module with mock provider
CI / Lint (push) Successful in 9m36s
CI / V2 Module (push) Successful in 11m33s
CI / Root Module (push) Successful in 11m35s
Cover all core library logic (Client, Model, Chat, middleware, streaming,
message conversion, request building) using a configurable mock provider
that avoids real API calls. ~50 tests across 7 files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 22:00:49 -05:00
steve cbe340ced0 Fix corrupted checksum for charmbracelet/bubbles in go.sum
CI / Lint (push) Successful in 9m34s
CI / V2 Module (push) Successful in 11m34s
CI / Root Module (push) Successful in 11m35s
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 21:39:32 -05:00
steve 9e288954f2 Add transcription API to v2 module
CI / Lint (push) Failing after 5m0s
CI / Root Module (push) Failing after 5m3s
CI / V2 Module (push) Successful in 10m48s
Migrate speech-to-text transcription types and OpenAI transcriber
implementation from v1. Types are defined in provider/ to avoid
import cycles and re-exported via type aliases from the root package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 20:24:20 -05:00
steve 9d6d2c61c3 Add Gitea CI workflow for build, test, vet, and lint
CI / Lint (push) Failing after 29s
CI / Root Module (push) Failing after 5m19s
CI / V2 Module (push) Successful in 11m9s
Runs on all pushes and PRs:
- Build, vet, and test both root and v2 modules (with -race)
- Verify go.mod/go.sum tidiness for both modules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 20:01:01 -05:00
steve a4cb4baab5 Add go-llm v2: redesigned API for simpler LLM abstraction
v2 is a new Go module (v2/) with a dramatically simpler API:
- Unified Message type (no more Input marker interface)
- Define[T] for ergonomic tool creation with standard context.Context
- Chat session with automatic tool-call loop (agent loop)
- Streaming via pull-based StreamReader
- MCP one-call connect (MCPStdioServer, MCPHTTPServer, MCPSSEServer)
- Middleware support (logging, retry, timeout, usage tracking)
- Decoupled JSON Schema (map[string]any, no provider coupling)
- Sample tools: WebSearch, Browser, Exec, ReadFile, WriteFile, HTTP
- Providers: OpenAI, Anthropic, Google (all with streaming)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 20:00:08 -05:00
steve 85a848d96e Update openaiTranscriber to handle audio file metadata in transcription parameters 2026-01-25 02:38:45 -05:00
steve 8801ce5945 Add OpenAI-based transcriber implementation
- Introduce `openaiTranscriber` for integrating OpenAI's Whisper audio transcription capabilities.
- Define `Transcriber` interface and associated types (`Transcription`, `TranscriptionOptions`, segments, and words).
- Implement transcription logic supporting features like languages, prompts, temperature, and timestamp granularities.
- Add `audioFileToWav` utility using `ffmpeg` for audio file conversion to WAV format.
- Ensure response parsing for structured and verbose JSON outputs.
2026-01-25 01:46:29 -05:00
steve 9c1b4f7e9f Fix checksum typo for github.com/charmbracelet/bubbles in go.sum 2026-01-24 16:59:55 -05:00
steve 2cf75ae07d Add MCP integration with MCPServer for tool-based interactions
- Introduce `MCPServer` to support connecting to MCP servers via stdio, SSE, or HTTP.
- Implement tool fetching, management, and invocation through MCP.
- Add `WithMCPServer` method to `ToolBox` for seamless tool integration.
- Extend schema package to handle raw JSON schemas for MCP tools.
- Update documentation with MCP usage guidelines and examples.
2026-01-24 16:25:28 -05:00
steve 97d54c10ae Implement interactive CLI for LLM providers with chat, tools, and image support
- Add Bubble Tea-based CLI interface for LLM interactions.
- Implement `.env.example` for environment variable setup.
- Add provider, model, and tool selection screens.
- Include support for API key configuration.
- Enable chat interactions with optional image and tool support.
- Introduce core utility functions: image handling, tool execution, chat request management, and response rendering.
- Implement style customization with Lip Gloss.
2026-01-24 15:53:36 -05:00
steve bf7c86ab2a Refactor: modularize and streamline LLM providers and utility functions
- Migrate `compress_image.go` to `internal/imageutil` for better encapsulation.
- Reorganize LLM provider implementations into distinct packages (`google`, `openai`, and `anthropic`).
- Replace `go_llm` package name with `llm`.
- Refactor internal APIs for improved clarity, including renaming `anthropic` to `anthropicImpl` and `google` to `googleImpl`.
- Add helper methods and restructure message handling for better separation of concerns.
2026-01-24 15:40:38 -05:00
steve be99af3597 Update all dependencies and migrate to new Google genai SDK
- Update all Go dependencies to latest versions
- Migrate from github.com/google/generative-ai-go/genai to google.golang.org/genai
- Fix google.go to use the new SDK API (NewPartFromText, NewContentFromParts, etc.)
- Update schema package imports to use the new genai package
- Add CLAUDE.md with README maintenance guideline

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 15:22:34 -05:00
steve 1927f4d187 woops messed up restriction 2025-08-08 11:07:40 -04:00
steve 5fa7c7e5c7 Restrict temperature override for unsupported models (o* and gpt-5*). 2025-08-08 10:37:09 -04:00
steve 07a04d08a9 Add WithDescription method to Function struct
Extend the `Function` struct with a `WithDescription` method to allow setting descriptions fluently.
2025-07-31 01:51:28 -04:00
Steve Dudenhoeffer 31766134ef bubble up the mime type 2025-07-21 23:14:55 -04:00
Steve Dudenhoeffer e0adc40661 fix junie's bad idea 2025-07-21 22:53:11 -04:00
Steve Dudenhoeffer c73c63a8aa Merge remote-tracking branch 'origin/main' 2025-07-21 21:53:21 -04:00
Steve Dudenhoeffer 101291abd9 handle base64 image resizing for anthropic 2025-07-21 21:53:13 -04:00
steve e9baf7910e Add LLM parsing functionality
Introduce `Providers` struct to handle different language model providers. Implement `Parse` method to extract and validate provider/model from input string, then return a chat completion interface. Add error handling for invalid formats or unknown providers.
2025-05-01 22:11:23 -04:00
steve 39ffb82237 Add image handling for Gemini requests with URL download and base64 support 2025-04-12 15:07:44 -04:00
steve 916f07be18 added anthropic tool support i think 2025-04-12 03:41:48 -04:00
steve 3093b988f8 Refactor toolbox and function handling to support synthetic fields and improve type definitions 2025-04-12 02:20:40 -04:00
steve 2ae583e9f3 Add support for required fields in parameter generation
Previously, required fields were not handled in OpenAI and Google parameter generation. This update adds logic to include a "required" list for both, ensuring mandatory fields are accurately captured in the schema outputs.
2025-04-07 20:33:21 -04:00
steve 5ba0d5df7e instead of having an openai => google translation layer, just add sister functions to the types that construct the google request just like openai's 2025-04-07 01:57:02 -04:00
steve 58552ee226 Fix enum typing 2025-04-06 15:35:05 -04:00