Skip to content

Commit 6025922

Browse files
authored
feat(llm): add bounded direct and tool turn runner (#107)
1 parent 4398aac commit 6025922

7 files changed

Lines changed: 1178 additions & 3 deletions

File tree

‎crates/players/gfa-llm/Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ serde_json.workspace = true
1010
sha2.workspace = true
1111
thiserror.workspace = true
1212
reqwest.workspace = true
13+
tokio.workspace = true
1314

1415
[dev-dependencies]
1516
axum.workspace = true
16-
tokio.workspace = true
1717
futures-util.workspace = true
1818

1919
[lints]

‎crates/players/gfa-llm/README.md‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,15 @@ Unit and loopback HTTP tests cover both wire formats, signed history, cache acco
1818

1919
Protocol references: [thinking](https://platform.claude.com/docs/en/build-with-claude/thinking), [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs), [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching), and [context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing).
2020

21-
Dependencies reuse serde/serde_json, SHA-256, and thiserror for serialization, stable agent fingerprints, and typed errors. Reqwest with Rustls supplies asynchronous HTTPS without a system OpenSSL dependency; Axum, Tokio, and futures-util are test-only dependencies for loopback fixtures. No game implementation or application transport adapter is imported.
21+
Dependencies reuse serde/serde_json, SHA-256, and thiserror for serialization, stable agent fingerprints, and typed errors. Reqwest with Rustls supplies asynchronous HTTPS without a system OpenSSL dependency; Tokio enforces the whole-move deadline; Axum and futures-util are test-only dependencies for loopback fixtures. No game implementation or application transport adapter is imported.
22+
23+
24+
## Turn runner
25+
26+
`PlayerSession::new` freezes the configured prompt, briefing, and tools. `play_turn` takes an already projected seat observation and its canonical legal actions. It returns a proposed move and a complete `TurnReport`; the service commits the move separately with its expected-turn precondition. The source session remains unchanged if the future is cancelled. Persist the returned session with the accepted move or failed-move policy.
27+
28+
Direct responses must be exactly the advertised action/reasoning JSON object. Tools mode offers `get_state` and `make_move`, plus `simulate_moves` and `analyze_position` only when permitted. Tool inputs cannot select another match or seat. The host binds the planning adapter to the same authorized service operations used by MCP. `make_move` stages one listed action, answers every tool-use ID in the response, and skips further work in that batch after choosing a move. Planning cannot invoke another LLM as an analysis engine.
29+
30+
The runner allows the configured illegal-move retries, enforces one output-token limit across all calls in the move, and applies the move deadline to both provider and planning calls. Sixteen calls per turn is an additional hard bound on endless planning. It returns explicit failures to the host instead of choosing a fallback move. Refusals, output exhaustion, invalid actions, and invalid formats are recorded as failed attempts. Provider errors and budget exhaustion are not silently retried. Full request/response pairs, tool results, per-class usage, and elapsed time are retained even on failure.
31+
32+
The host still owns spending reservations, pricing, durable per-call journals, concurrency, and match policy. It must reserve and journal each call before the provider sees it, because a process crash can prevent an in-memory report from being returned. Unknown billing after a timeout keeps its reservation. A cancelled turn does not rewrite the last committed conversation; failed or uncommitted calls remain in the durable audit log. This library does not enable LLM seats in the server by itself.

‎crates/players/gfa-llm/src/lib.rs‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod accounting;
44
mod anthropic;
55
mod config;
66
mod provider;
7+
mod runner;
78

89
pub use accounting::{Budget, Pricing, Usage};
910
pub use anthropic::AnthropicProvider;
@@ -12,6 +13,10 @@ pub use provider::{
1213
ContentMessage, LlmProvider, ProviderError, ProviderFuture, ProviderRequest, ProviderResponse,
1314
Role, StopReason, ToolDefinition,
1415
};
16+
pub use runner::{
17+
AllowedAssists, CallRecord, FailedAttempt, MoveDecision, PlayerSession, ToolExchange,
18+
ToolFuture, ToolResult, TurnContext, TurnFailure, TurnOutcome, TurnReport, TurnTools,
19+
};
1520

1621
#[cfg(test)]
1722
mod tests;

‎crates/players/gfa-llm/src/provider.rs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use serde_json::Value;
44
use std::{future::Future, pin::Pin};
55

66
/// Errors never carry credentials or arbitrary HTTP response bodies.
7-
#[derive(Clone, Copy, Debug, PartialEq, Eq, thiserror::Error)]
7+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, thiserror::Error)]
8+
#[serde(rename_all = "snake_case")]
89
pub enum ProviderError {
910
/// Invalid host or player settings.
1011
#[error("invalid LLM configuration")]

0 commit comments

Comments
 (0)