generated from nhcarrigan/template
feat: expose prompt caching TTL setting in UI (#273)
Adds a dropdown to select ENABLE_PROMPT_CACHING_1H or FORCE_PROMPT_CACHING_5M env vars when starting Claude Code sessions. Requires Claude Code v2.1.108+.
This commit is contained in:
@@ -75,6 +75,11 @@ pub struct ClaudeStartOptions {
|
||||
/// Valid values: "low", "medium", "high", "xhigh" (Opus 4.7 only), "max". None uses CLI default.
|
||||
#[serde(default)]
|
||||
pub effort_level: Option<String>,
|
||||
|
||||
/// Sets `ENABLE_PROMPT_CACHING_1H=1` or `FORCE_PROMPT_CACHING_5M=1` env var (v2.1.108+).
|
||||
/// Values: "1h" → 1-hour TTL, "5m" → force 5-minute TTL. None uses CLI default.
|
||||
#[serde(default)]
|
||||
pub prompt_caching_ttl: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -246,6 +251,11 @@ pub struct HikariConfig {
|
||||
/// Valid values: "low", "medium", "high", "xhigh" (Opus 4.7 only), "max". None uses CLI default.
|
||||
#[serde(default)]
|
||||
pub effort_level: Option<String>,
|
||||
|
||||
/// Sets `ENABLE_PROMPT_CACHING_1H=1` or `FORCE_PROMPT_CACHING_5M=1` env var (v2.1.108+).
|
||||
/// Values: "1h" → 1-hour TTL, "5m" → force 5-minute TTL. None uses CLI default.
|
||||
#[serde(default)]
|
||||
pub prompt_caching_ttl: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for HikariConfig {
|
||||
@@ -302,6 +312,7 @@ impl Default for HikariConfig {
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: None,
|
||||
effort_level: None,
|
||||
prompt_caching_ttl: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,6 +531,7 @@ mod tests {
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: None,
|
||||
effort_level: None,
|
||||
prompt_caching_ttl: None,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&config).unwrap();
|
||||
|
||||
@@ -410,6 +410,15 @@ impl WslBridge {
|
||||
}
|
||||
}
|
||||
|
||||
// Set prompt caching TTL env var if specified (v2.1.108+)
|
||||
if let Some(ref ttl) = options.prompt_caching_ttl {
|
||||
match ttl.as_str() {
|
||||
"1h" => { cmd.env("ENABLE_PROMPT_CACHING_1H", "1"); }
|
||||
"5m" => { cmd.env("FORCE_PROMPT_CACHING_5M", "1"); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
cmd
|
||||
} else {
|
||||
// Running on Windows - use wsl with bash login shell to ensure PATH is loaded
|
||||
@@ -485,6 +494,15 @@ impl WslBridge {
|
||||
}
|
||||
}
|
||||
|
||||
// Set prompt caching TTL env var if specified (v2.1.108+)
|
||||
if let Some(ref ttl) = options.prompt_caching_ttl {
|
||||
match ttl.as_str() {
|
||||
"1h" => { claude_cmd.push_str("ENABLE_PROMPT_CACHING_1H=1 "); }
|
||||
"5m" => { claude_cmd.push_str("FORCE_PROMPT_CACHING_5M=1 "); }
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
claude_cmd.push_str(
|
||||
"claude --output-format stream-json --input-format stream-json --verbose",
|
||||
);
|
||||
|
||||
@@ -78,6 +78,7 @@ async function changeDirectory(path: string): Promise<void> {
|
||||
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: config.custom_model_option || null,
|
||||
effort_level: config.effort_level || null,
|
||||
prompt_caching_ttl: config.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -166,6 +167,7 @@ async function startNewConversation(): Promise<void> {
|
||||
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: config.custom_model_option || null,
|
||||
effort_level: config.effort_level || null,
|
||||
prompt_caching_ttl: config.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
task_loop_commit_prefix: "feat",
|
||||
task_loop_include_summary: false,
|
||||
effort_level: null,
|
||||
prompt_caching_ttl: null,
|
||||
});
|
||||
|
||||
let showCustomThemeEditor = $state(false);
|
||||
@@ -740,6 +741,26 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Prompt Caching TTL -->
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm text-[var(--text-primary)] mb-1" for="prompt-caching-ttl"
|
||||
>Prompt caching TTL</label
|
||||
>
|
||||
<select
|
||||
id="prompt-caching-ttl"
|
||||
bind:value={config.prompt_caching_ttl}
|
||||
class="w-full px-3 py-2 rounded border border-[var(--border-color)] bg-[var(--bg-primary)] text-[var(--text-primary)] text-sm focus:outline-none focus:ring-1 focus:ring-[var(--accent-primary)]"
|
||||
>
|
||||
<option value={null}>Default (CLI decides)</option>
|
||||
<option value="1h">1-hour TTL (reduces cost for long sessions)</option>
|
||||
<option value="5m">Force 5-minute TTL</option>
|
||||
</select>
|
||||
<p class="text-xs text-[var(--text-tertiary)] mt-1">
|
||||
Sets <code class="font-mono">ENABLE_PROMPT_CACHING_1H</code> or
|
||||
<code class="font-mono">FORCE_PROMPT_CACHING_5M</code>. Requires Claude Code v2.1.108+.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Bare Mode -->
|
||||
<div class="mb-4">
|
||||
<label class="flex items-center gap-3 cursor-pointer">
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: config.custom_model_option || null,
|
||||
effort_level: config.effort_level || null,
|
||||
prompt_caching_ttl: config.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -413,6 +413,7 @@ User: ${formattedMessage}`;
|
||||
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: config.custom_model_option || null,
|
||||
effort_level: config.effort_level || null,
|
||||
prompt_caching_ttl: config.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: config.custom_model_option || null,
|
||||
effort_level: config.effort_level || null,
|
||||
prompt_caching_ttl: config.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: null,
|
||||
effort_level: null,
|
||||
prompt_caching_ttl: null,
|
||||
});
|
||||
|
||||
let streamerModeActive = $state(false);
|
||||
@@ -191,6 +192,7 @@
|
||||
currentConfig.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: currentConfig.custom_model_option || null,
|
||||
effort_level: currentConfig.effort_level || null,
|
||||
prompt_caching_ttl: currentConfig.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -360,6 +362,7 @@
|
||||
currentConfig.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: currentConfig.custom_model_option || null,
|
||||
effort_level: currentConfig.effort_level || null,
|
||||
prompt_caching_ttl: currentConfig.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -229,6 +229,7 @@
|
||||
show_clear_context_on_plan_accept: cfg.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: cfg.custom_model_option || null,
|
||||
effort_level: cfg.effort_level || null,
|
||||
prompt_caching_ttl: cfg.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
show_clear_context_on_plan_accept: config.show_clear_context_on_plan_accept ?? true,
|
||||
custom_model_option: config.custom_model_option || null,
|
||||
effort_level: config.effort_level || null,
|
||||
prompt_caching_ttl: config.prompt_caching_ttl || null,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -230,6 +230,7 @@ describe("config store", () => {
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: null,
|
||||
effort_level: null,
|
||||
prompt_caching_ttl: null,
|
||||
};
|
||||
|
||||
expect(config.model).toBe("claude-sonnet-4");
|
||||
@@ -299,6 +300,7 @@ describe("config store", () => {
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: null,
|
||||
effort_level: null,
|
||||
prompt_caching_ttl: null,
|
||||
};
|
||||
|
||||
expect(config.model).toBeNull();
|
||||
@@ -923,6 +925,7 @@ describe("config store", () => {
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: null,
|
||||
effort_level: null,
|
||||
prompt_caching_ttl: null,
|
||||
};
|
||||
|
||||
const mockInvokeImpl = vi.mocked(invoke);
|
||||
|
||||
@@ -101,6 +101,8 @@ export interface HikariConfig {
|
||||
custom_model_option: string | null;
|
||||
// Effort level for Claude Code (v2.1.111+) — null means use CLI default
|
||||
effort_level: string | null;
|
||||
// Prompt caching TTL override (v2.1.108+) — null means use CLI default
|
||||
prompt_caching_ttl: string | null;
|
||||
}
|
||||
|
||||
const defaultConfig: HikariConfig = {
|
||||
@@ -164,6 +166,7 @@ const defaultConfig: HikariConfig = {
|
||||
show_clear_context_on_plan_accept: true,
|
||||
custom_model_option: null,
|
||||
effort_level: null,
|
||||
prompt_caching_ttl: null,
|
||||
};
|
||||
|
||||
function createConfigStore() {
|
||||
|
||||
Reference in New Issue
Block a user