生成設定のmanifest化の実装

This commit is contained in:
2026-04-28 09:37:22 +09:00
parent 7a0ed7d744
commit bdf2a08459
8 changed files with 206 additions and 16 deletions
+1 -1
View File
@@ -9,6 +9,6 @@ Pod の宣言的設定を TOML マニフェストとして定義・パースす
- `ModelConfig` — LLM モデル設定(scheme、base_url、model_id、auth
- `SchemeKind` — wire scheme 種別(`Anthropic`, `OpenaiChat`, `OpenaiResponses`, `Gemini`
- `AuthRef` — 認証参照(`None`, `ApiKey { env, file }`, `CodexOAuth`
- `WorkerManifest` — ワーカー設定(システムプロンプト、max_tokens、temperature
- `WorkerManifest` — ワーカー設定(システムプロンプト、生成設定、reasoning
- `ScopeConfig` / `ScopeRule` / `Permission` — allow / deny の宣言的スコープ設定
- `Scope` — 実行時スコープ。`from_config(&ScopeConfig, pwd)` で構築し、`is_readable` / `is_writable` / `permission_at` で問い合わせる
+59
View File
@@ -65,6 +65,12 @@ pub struct WorkerManifestConfig {
#[serde(default)]
pub temperature: Option<f32>,
#[serde(default)]
pub top_p: Option<f32>,
#[serde(default)]
pub top_k: Option<u32>,
#[serde(default)]
pub stop_sequences: Option<Vec<String>>,
#[serde(default)]
pub reasoning: Option<ReasoningControl>,
#[serde(default)]
pub tool_output: ToolOutputLimitsPartial,
@@ -226,6 +232,9 @@ impl WorkerManifestConfig {
max_tokens: upper.max_tokens.or(self.max_tokens),
max_turns: upper.max_turns.or(self.max_turns),
temperature: upper.temperature.or(self.temperature),
top_p: upper.top_p.or(self.top_p),
top_k: upper.top_k.or(self.top_k),
stop_sequences: upper.stop_sequences.or(self.stop_sequences),
reasoning: upper.reasoning.or(self.reasoning),
tool_output: self.tool_output.merge(upper.tool_output),
}
@@ -339,6 +348,9 @@ impl TryFrom<PodManifestConfig> for PodManifest {
max_tokens: cfg.worker.max_tokens,
max_turns: cfg.worker.max_turns,
temperature: cfg.worker.temperature,
top_p: cfg.worker.top_p,
top_k: cfg.worker.top_k,
stop_sequences: cfg.worker.stop_sequences.unwrap_or_default(),
reasoning: cfg.worker.reasoning,
tool_output: ToolOutputLimits {
default_max_bytes: cfg
@@ -586,6 +598,33 @@ mod tests {
);
}
#[test]
fn merge_worker_generation_settings_upper_wins() {
let lower = PodManifestConfig {
worker: WorkerManifestConfig {
top_p: Some(0.8),
top_k: Some(20),
stop_sequences: Some(vec!["lower".into()]),
..Default::default()
},
..Default::default()
};
let upper = PodManifestConfig {
worker: WorkerManifestConfig {
top_p: Some(0.9),
stop_sequences: Some(vec!["upper".into()]),
..Default::default()
},
..Default::default()
};
let merged = lower.merge(upper);
assert_eq!(merged.worker.top_p, Some(0.9));
assert_eq!(merged.worker.top_k, Some(20));
assert_eq!(merged.worker.stop_sequences, Some(vec!["upper".into()]));
}
#[test]
fn merge_scope_accumulates_allow_and_deny() {
let lower = PodManifestConfig {
@@ -732,6 +771,26 @@ reasoning = -1
);
}
#[test]
fn from_toml_accepts_worker_generation_settings() {
let cfg = PodManifestConfig::from_toml(
r#"
[worker]
top_p = 0.9
top_k = 40
stop_sequences = ["\n\n", "</stop>"]
"#,
)
.unwrap();
assert_eq!(cfg.worker.top_p, Some(0.9));
assert_eq!(cfg.worker.top_k, Some(40));
assert_eq!(
cfg.worker.stop_sequences,
Some(vec!["\n\n".into(), "</stop>".into()])
);
}
#[test]
fn from_toml_partial_layer_succeeds() {
// A project-layer manifest with only scope set must parse fine.
+15
View File
@@ -102,6 +102,12 @@ pub struct WorkerManifest {
#[serde(default)]
pub temperature: Option<f32>,
#[serde(default)]
pub top_p: Option<f32>,
#[serde(default)]
pub top_k: Option<u32>,
#[serde(default)]
pub stop_sequences: Vec<String>,
#[serde(default)]
pub reasoning: Option<ReasoningControl>,
/// Byte-size caps applied to tool `content` before it reaches the
/// conversation history. The section is optional in TOML — when
@@ -299,6 +305,9 @@ permission = "write"
assert_eq!(manifest.scope.allow.len(), 1);
assert!(manifest.scope.deny.is_empty());
assert_eq!(manifest.worker.instruction, defaults::DEFAULT_INSTRUCTION);
assert!(manifest.worker.top_p.is_none());
assert!(manifest.worker.top_k.is_none());
assert!(manifest.worker.stop_sequences.is_empty());
}
#[test]
@@ -316,6 +325,9 @@ auth = { kind = "api_key", file = "/abs/keys/anthropic" }
instruction = "$user/reviewer"
max_tokens = 4096
temperature = 0.3
top_p = 0.9
top_k = 40
stop_sequences = ["\n\n", "</stop>"]
reasoning = "medium"
[[scope.allow]]
@@ -341,6 +353,9 @@ permission = "write"
assert_eq!(manifest.worker.instruction, "$user/reviewer");
assert_eq!(manifest.worker.max_tokens, Some(4096));
assert_eq!(manifest.worker.temperature, Some(0.3));
assert_eq!(manifest.worker.top_p, Some(0.9));
assert_eq!(manifest.worker.top_k, Some(40));
assert_eq!(manifest.worker.stop_sequences, vec!["\n\n", "</stop>"]);
assert_eq!(
manifest.worker.reasoning,
Some(ReasoningControl::Effort(ReasoningEffort::Medium))