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
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ Runtime generation is split by responsibility:
cache/decode, and shutdown work serialized while heavy generation runs away from MainActor. The
manager should publish state; the core should own native correctness.

Cotabby owns one autocomplete sequence. CotabbyInference therefore exposes one live native sequence
backed by llama.cpp slot zero; a changing external sequence ID rejects stale handles after reset.
The Swift generation loop owns the maximum output-token budget.

## UI And Overlays

- `OverlayController` owns the ghost-text panel lifecycle and positioning.
Expand Down
4 changes: 4 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ LlamaRuntimeCore is a nonisolated lock/condition-protected native boundary, not
autocomplete lock serializes cache/decode state, and its lifecycle condition prevents shutdown from
racing active native work. Heavy work runs away from MainActor.

The app and CotabbyInference intentionally expose one live autocomplete sequence. The package uses
one llama.cpp sequence slot and a changing external ID to reject stale work after a reset; the Swift
generation loop remains the single owner of the output-token budget.

[BaseCompletionPromptRenderer.swift](Cotabby/Support/Prompting/BaseCompletionPromptRenderer.swift) renders a
base-model text continuation with optional budgeted context and the caret prefix last. It does not
wrap a base GGUF in an instruction conversation. [FoundationModelPromptRenderer.swift](Cotabby/Support/Prompting/FoundationModelPromptRenderer.swift)
Expand Down
35 changes: 19 additions & 16 deletions Cotabby/Services/Runtime/LlamaRuntimeCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import CotabbyInference

/// File overview:
/// Owns the C++ inference engine and manages the autocomplete KV cache lifecycle. This is the
/// lowest-level runtime boundary in the app: it loads the GGUF model, manages concurrent
/// sequences, tokenizes prompts, samples continuations, and frees native resources on shutdown.
/// lowest-level runtime boundary in the app: it loads the GGUF model, owns Cotabby's single
/// autocomplete sequence, tokenizes prompts, samples continuations, and frees native resources on
/// shutdown.
///
/// The engine handles thread safety internally via per-sequence mutexes. This class is
/// `@unchecked Sendable` rather than an `actor` so native work can execute away from MainActor.
/// `autocompleteLock` serializes autocomplete-specific KV-cache state. A separate
/// `lifecycleCondition` prevents `shutdown()` from unloading the model while generation is in flight.
/// The engine serializes its one mutable llama context internally. This class is `@unchecked
/// Sendable` rather than an `actor` so native work can execute away from MainActor.
/// `autocompleteLock` serializes autocomplete-specific KV-cache state, while a separate
/// `lifecycleCondition` prevents `shutdown()` from unloading the model during generation.

/// Immutable runtime metadata captured after a model has been successfully prepared.
struct PreparedLlamaRuntime: Sendable {
Expand Down Expand Up @@ -717,16 +718,18 @@ nonisolated final class LlamaRuntimeCore: @unchecked Sendable {
private static let defaultSamplerSeed: UInt32 = 0x00C0_FFEE

private static func samplingConfig(from options: LlamaGenerationOptions) -> SamplingConfig {
SamplingConfig(
max_prediction_tokens: Int32(options.maxPredictionTokens),
temperature: Float(options.temperature),
top_k: Int32(options.topK),
top_p: Float(options.topP),
min_p: Float(options.minP),
repetition_penalty: Float(options.repetitionPenalty),
seed: options.seed ?? Self.defaultSamplerSeed,
single_line: options.singleLine
)
// Assign the fields after default construction so the app remains source-compatible while
// CotabbyInference removes native configuration fields that Swift never consumed. C++
// aggregate memberwise initializers otherwise require every imported field at the call site.
var config = SamplingConfig()
config.temperature = Float(options.temperature)
config.top_k = Int32(options.topK)
config.top_p = Float(options.topP)
config.min_p = Float(options.minP)
config.repetition_penalty = Float(options.repetitionPenalty)
config.seed = options.seed ?? Self.defaultSamplerSeed
config.single_line = options.singleLine
return config
}
Comment on lines 720 to 733

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 max_prediction_tokens silently dropped from C++ boundary config. The old call explicitly passed Int32(options.maxPredictionTokens) as max_prediction_tokens; default-constructing SamplingConfig() leaves that field at its C++ aggregate zero-initialization default (0). The Swift for _ in 0 ..< options.maxPredictionTokens loop at line 373 is the authoritative budget owner and is unchanged, so this is fine as long as sampleNext is stateless with respect to max_prediction_tokens. If a future C++ revision adds an internal guard in sampleNext that reads the sequence's stored config limit and short-circuits on 0, every decode would silently produce zero tokens. Since the PR validates builds against both package revisions and the comment calls this field "never consumed," this is currently safe, but worth flagging for reviewers of the companion CotabbyInference change.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code


private static func reusableTokenCount(commonTokenPrefix: Int, newPromptTokenCount: Int) -> Int {
Expand Down
Loading