Skip to content

Commit 4112432

Browse files
authored
feat(onboard): clean-slate rewrite — schema-driven, idempotent, DRY (zeroclaw-labs#5960)
- 0c622e6 feat(onboard): scorched-earth delete old wizard + TUI twin (zeroclaw-labs#5951) - 3dabdb1 feat(config): add async OnboardUi trait in traits.rs (zeroclaw-labs#5951) - 65e9809 feat(onboard): add TermUi — dialoguer OnboardUi backend (zeroclaw-labs#5951) - 95b8db4 feat(onboard): add QuickUi — headless OnboardUi backend (zeroclaw-labs#5951) - bcd3df1 feat(onboard): scaffold orchestrator — Section, Flags, run() dispatch… - d14cd46 feat(onboard): implement workspace section (zeroclaw-labs#5951) - 043dd8a feat(onboard): implement memory section; drop Project variant (zeroclaw-labs#5951) - 7b5a4b3 feat(onboard): DRY up memory + implement tunnel via existing sources… - 26251b7 feat(onboard): add generic prompt_field + prompt_fields_under helpers… - 1ada5fb feat(onboard): implement hardware section via prop_fields only (zeroclaw-labs#5951) - fe836a7 feat(onboard): implement providers section via list_providers() + pro… - c22d8c5 feat(onboard): TermUi select uses FuzzySelect for searchable menus (#… - b397324 feat(providers): no-auth live model listing via models.dev + native p… - ff4183a feat(onboard): wire live model picker into providers section (zeroclaw-labs#5951) - 415fe0b feat(onboard): implement channels section via prop_fields + free-text… - acb4658 feat(tui): thin RatatuiUi implementing OnboardUi (zeroclaw-labs#5951) - b7a99db feat(cli): wire zeroclaw onboard to the new orchestrator (zeroclaw-labs#5951) - 2710fbc chore(onboard): pass cargo fmt + clippy --workspace -D warnings gate… - e11191f fix(config): route get_prop/set_prop through HashMap<String, T> + Ter… - 3f8f30b feat(onboard): human-readable prompts from doc comments + consolidate… - 8fee0f7 fix(tui): wrap prompt text + grow prompt bar to fit (zeroclaw-labs#5951) - 8979fdd fix(onboard): show field docstring as context above the prompt, not i… - 045388d docs(config): rewrite WorkspaceConfig + HardwareConfig doc comments f… - b27c4ea fix(onboard): channels menu derives full list from schema via probe c… - eb97efc feat(tui): restore ZeroClaw ASCII banner at top of RatatuiUi (zeroclaw-labs#5951) - 514a77c fix(onboard): reset context panel between sections, note=replace (zeroclaw-labs#5951) - c9c47d5 feat(onboard): persist config after every write so mid-flow aborts r… - fc5ad62 fix(tui): select list uses ratatui List + ListState for scroll-aware… - 59512f1 feat(onboard): section-level skip gate for already-configured section… - 8abab39 feat(onboard): track completed sections via onboard_state.completed_s… - 576ac39 fix(onboard): surface HashMap fields, Back nav, Markdown-style phase… - 38d55f4 refactor(providers): Option<f64> temperature + per-family default_* o… - 0a43a14 feat(onboard): six-region TUI layout, provider-defaults prefill, adva… - 1f50f68 fix(cli): wrap final_temperature in Some for Provider::simple_chat (#… - 1954cf1 test(onboard): skip-gate + idempotency coverage, fix channels signal… - 696e95d test(onboard): cover legacy flag shim and provider flag path (zeroclaw-labs#5951) - f0a2923 fix(onboard): bool prompts and multi-line help text rendering (zeroclaw-labs#5951) - e13d06e fix(onboard): flatten bool is_set so default annotations reach the l… - 2188306 feat(onboard): paint status/warn immediately so web-fetch wait is vi… - 5c2b3fd fix(onboard,doctor): restore zeroclaw doctor models; drop hardcoded m… - 651573f fix(onboard): rustfmt line-length in doctor summary counters - f3e0e48 fix(onboard): honor Answer::Back contract on editor close-without-save - 10c5b7e fix(channels): gate Mochat registration on channels.mochat.enabled - 53ab2a3 feat(cli): restore zeroclaw models via doctor::run_models routing - 73be86e feat(onboard): default to TUI; add --cli and TUI init fallback - 063b23a feat(onboard): improve models.dev fetch error UX - be4688a refactor(config): omit default-valued flags from serialized ModelProv… - e727afa feat(onboard): filter advanced-walk fields by provider family - bd12473 test(onboard): per-channel smoke tests for telegram + mochat - ba0bbbc docs(changelog): document onboard + provider config + CLI changes - 6abb3ce style: remove extra blank line in src/config/mod.rs (cargo fmt) - 50a9bab fix(main): handle Windows paths in desktop locator, daemon-from-home… - e14b30e fix(doctor): fall back to %ComSpec% on Windows when checking shell - 72447ac fix(config): make AutonomyConfig defaults Windows-aware - a0f0eea docs(extension-examples): update Provider example to Option<f64> temp… - 5532018 fix(onboard): no eager writes; complete provider flow before any persist
1 parent 00440cd commit 4112432

65 files changed

Lines changed: 3972 additions & 13550 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG-next.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@
113113
- Slack config: `channel_id` deprecated in favour of `channel_ids` (plural) for V2.
114114
- Nostr, WhatsApp Web, and hardware wizard sections wired into the onboarding flow
115115
(#5640).
116+
- Fresh `[providers.models.<name>]` entries no longer serialize family-irrelevant
117+
default flags: `requires_openai_auth = false`, `merge_system_into_user = false`,
118+
and empty `name`/`base_url`/`wire_api` entries are now omitted when they carry
119+
their struct-level defaults. Existing configs deserialize unchanged.
116120

117121
### Observability
118122

@@ -147,6 +151,27 @@
147151
- Fixed: `cron_run` tool output was not being delivered to configured channels.
148152
- Windows: the shell console window is now hidden when running as a background process
149153
(#5563).
154+
- `zeroclaw onboard` now defaults to the ratatui TUI backend. Pass `--cli` to force the
155+
terminal-prompt backend; `--tui` is accepted as a deprecated no-op for one release.
156+
Falls back to the terminal-prompt backend automatically when stdout isn't a TTY or
157+
ratatui init fails.
158+
- `zeroclaw onboard` advanced-settings walk now filters out fields that don't apply to
159+
the selected provider family — no more `azure_openai_*` prompts when configuring
160+
Anthropic, no more `wire_api` / `requires_openai_auth` prompts outside the OpenAI
161+
family.
162+
- `zeroclaw onboard` model-catalog fetch failures now name the provider in the fallback
163+
note (`"Catalog lookup failed for <provider> — enter a model id manually"`) and log
164+
the underlying error at `debug` level instead of swallowing it entirely.
165+
- `zeroclaw models` restored — routes to the same live model-probe output as
166+
`zeroclaw doctor models`, preserving the `--provider` flag for `models list` /
167+
`models refresh` subcommands.
168+
- Fixed: `TermUi::editor` close-without-save now returns `Answer::Back` (rewind)
169+
instead of accepting the unchanged buffer silently. Matches the navigation contract
170+
every other prompt method honors.
171+
- Fixed: `channels.mochat` now respects its `enabled` flag. Previously the orchestrator
172+
registered the Mochat channel whenever the `[channels.mochat]` section existed,
173+
producing `Mochat: poll request error: builder error` log spam on invalid or empty
174+
URLs. Matches the WeCom / ClawdTalk enabled-gate pattern.
150175

151176
### Skills (Claude Code)
152177

@@ -231,6 +256,14 @@ release but will not be supported indefinitely.
231256
Use `zeroclaw config` instead. The `props` subcommand still works and will not be
232257
removed in this release, but it will emit a deprecation notice.
233258

259+
### `zeroclaw onboard` defaults to TUI
260+
261+
`zeroclaw onboard` now launches the ratatui TUI backend by default. Users who were
262+
relying on the old terminal-prompt behavior should pass `--cli` explicitly. The
263+
previous `--tui` flag is accepted for one release as a deprecated no-op and emits a
264+
warning pointing at the new default. In non-TTY environments (piped output, CI without
265+
`--quick`) onboarding automatically falls back to the terminal-prompt backend.
266+
234267
### Slack `channel_id` deprecated
235268

236269
Use `channel_ids` (a list) in the Slack config block. `channel_id` (singular) still

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

benches/agent_benchmarks.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl Provider for BenchProvider {
7676
_system_prompt: Option<&str>,
7777
_message: &str,
7878
_model: &str,
79-
_temperature: f64,
79+
_temperature: Option<f64>,
8080
) -> Result<String> {
8181
Ok("fallback".into())
8282
}
@@ -85,7 +85,7 @@ impl Provider for BenchProvider {
8585
&self,
8686
_request: ChatRequest<'_>,
8787
_model: &str,
88-
_temperature: f64,
88+
_temperature: Option<f64>,
8989
) -> Result<ChatResponse> {
9090
let mut guard = self.responses.lock().unwrap();
9191
if guard.is_empty() {

crates/zeroclaw-api/src/provider.rs

Lines changed: 118 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -295,13 +295,70 @@ pub enum ToolsPayload {
295295
PromptGuided { instructions: String },
296296
}
297297

298+
/// Industry-neutral sampling temperature. OpenAI, Gemini, OpenRouter, and
299+
/// most OpenAI-compatible endpoints document 0.7 as their typical default;
300+
/// Anthropic and Ollama override (1.0 and 0.0 respectively).
301+
pub const BASELINE_TEMPERATURE: f64 = 0.7;
302+
303+
/// Output-token budget roomy enough for typical agent turns. Providers
304+
/// override per family where the model's own context window is the
305+
/// binding constraint.
306+
pub const BASELINE_MAX_TOKENS: u32 = 4096;
307+
308+
/// HTTP timeout for cloud inference. Local providers (Ollama) override
309+
/// upward since CPU/GPU-bound inference runs slower than round-tripping to
310+
/// a hyperscaler.
311+
pub const BASELINE_TIMEOUT_SECS: u64 = 120;
312+
313+
/// Wire protocol used when the provider doesn't declare one. Only OpenAI's
314+
/// Codex stack uses the "responses" protocol; everything else speaks the
315+
/// classic chat completions shape.
316+
pub const BASELINE_WIRE_API: &str = "chat_completions";
317+
298318
#[async_trait]
299319
pub trait Provider: Send + Sync {
300320
/// Query provider capabilities.
301321
fn capabilities(&self) -> ProviderCapabilities {
302322
ProviderCapabilities::default()
303323
}
304324

325+
// ── Provider-family defaults ────────────────────────────────────────────
326+
// Called by every chat/stream method's `temperature.unwrap_or(self.default_temperature())`
327+
// and by `zeroclaw onboard` to prefill prompts with a visible default.
328+
// Baselines are the industry-neutral fallback; override per family where
329+
// the API docs disagree (Anthropic → 1.0, Ollama → 0.0 for deterministic
330+
// local inference).
331+
332+
/// Temperature used when the caller passes `None`. Override per family.
333+
fn default_temperature(&self) -> f64 {
334+
BASELINE_TEMPERATURE
335+
}
336+
337+
/// Max output tokens used when the caller / config doesn't set one.
338+
fn default_max_tokens(&self) -> u32 {
339+
BASELINE_MAX_TOKENS
340+
}
341+
342+
/// HTTP timeout (seconds) used when the caller / config doesn't set one.
343+
fn default_timeout_secs(&self) -> u64 {
344+
BASELINE_TIMEOUT_SECS
345+
}
346+
347+
/// Canonical public API endpoint, when there is one. Returned as a
348+
/// string slice so provider impls can serve from `const &'static str`s
349+
/// without allocations. `None` = provider has no universal endpoint
350+
/// (local providers, auth-less CLIs, user-BYO endpoints).
351+
fn default_base_url(&self) -> Option<&str> {
352+
None
353+
}
354+
355+
/// Wire protocol variant. Either `"responses"` (OpenAI Codex-style) or
356+
/// `"chat_completions"` (everything else). Providers override to their
357+
/// native format.
358+
fn default_wire_api(&self) -> &str {
359+
BASELINE_WIRE_API
360+
}
361+
305362
/// Convert tool specifications to provider-native format.
306363
fn convert_tools(&self, tools: &[ToolSpec]) -> ToolsPayload {
307364
ToolsPayload::PromptGuided {
@@ -310,31 +367,46 @@ pub trait Provider: Send + Sync {
310367
}
311368

312369
/// Simple one-shot chat (single user message, no explicit system prompt).
370+
///
371+
/// `temperature == None` means "use `self.default_temperature()`". The
372+
/// unwrap lives inside each provider impl, not at every call site.
313373
async fn simple_chat(
314374
&self,
315375
message: &str,
316376
model: &str,
317-
temperature: f64,
377+
temperature: Option<f64>,
318378
) -> anyhow::Result<String> {
319379
self.chat_with_system(None, message, model, temperature)
320380
.await
321381
}
322382

323-
/// One-shot chat with optional system prompt.
383+
/// One-shot chat with optional system prompt. See `simple_chat` for
384+
/// the `temperature` contract.
324385
async fn chat_with_system(
325386
&self,
326387
system_prompt: Option<&str>,
327388
message: &str,
328389
model: &str,
329-
temperature: f64,
390+
temperature: Option<f64>,
330391
) -> anyhow::Result<String>;
331392

332-
/// Multi-turn conversation.
393+
/// Fetch the list of available model IDs for this provider.
394+
///
395+
/// Used by onboard to present a live model picker. Default bails with
396+
/// "not supported"; concrete providers override to hit their own public
397+
/// endpoint (OpenRouter, Ollama) or delegate to the shared models.dev
398+
/// catalog (no auth required) in `zeroclaw_providers::models_dev`.
399+
async fn list_models(&self) -> anyhow::Result<Vec<String>> {
400+
anyhow::bail!("live model listing is not supported for this provider")
401+
}
402+
403+
/// Multi-turn conversation. See `simple_chat` for the `temperature`
404+
/// contract.
333405
async fn chat_with_history(
334406
&self,
335407
messages: &[ChatMessage],
336408
model: &str,
337-
temperature: f64,
409+
temperature: Option<f64>,
338410
) -> anyhow::Result<String> {
339411
let system = messages
340412
.iter()
@@ -349,12 +421,13 @@ pub trait Provider: Send + Sync {
349421
.await
350422
}
351423

352-
/// Structured chat API for agent loop callers.
424+
/// Structured chat API for agent loop callers. See `simple_chat` for
425+
/// the `temperature` contract.
353426
async fn chat(
354427
&self,
355428
request: ChatRequest<'_>,
356429
model: &str,
357-
temperature: f64,
430+
temperature: Option<f64>,
358431
) -> anyhow::Result<ChatResponse> {
359432
if let Some(tools) = request.tools
360433
&& !tools.is_empty()
@@ -418,12 +491,13 @@ pub trait Provider: Send + Sync {
418491
}
419492

420493
/// Chat with tool definitions for native function calling support.
494+
/// See `simple_chat` for the `temperature` contract.
421495
async fn chat_with_tools(
422496
&self,
423497
messages: &[ChatMessage],
424498
_tools: &[serde_json::Value],
425499
model: &str,
426-
temperature: f64,
500+
temperature: Option<f64>,
427501
) -> anyhow::Result<ChatResponse> {
428502
let text = self.chat_with_history(messages, model, temperature).await?;
429503
Ok(ChatResponse {
@@ -444,24 +518,26 @@ pub trait Provider: Send + Sync {
444518
false
445519
}
446520

447-
/// Streaming chat with optional system prompt.
521+
/// Streaming chat with optional system prompt. See `simple_chat` for
522+
/// the `temperature` contract.
448523
fn stream_chat_with_system(
449524
&self,
450525
_system_prompt: Option<&str>,
451526
_message: &str,
452527
_model: &str,
453-
_temperature: f64,
528+
_temperature: Option<f64>,
454529
_options: StreamOptions,
455530
) -> stream::BoxStream<'static, StreamResult<StreamChunk>> {
456531
stream::empty().boxed()
457532
}
458533

459-
/// Streaming chat with history.
534+
/// Streaming chat with history. See `simple_chat` for the `temperature`
535+
/// contract.
460536
fn stream_chat_with_history(
461537
&self,
462538
messages: &[ChatMessage],
463539
model: &str,
464-
temperature: f64,
540+
temperature: Option<f64>,
465541
options: StreamOptions,
466542
) -> stream::BoxStream<'static, StreamResult<StreamChunk>> {
467543
let system = messages
@@ -476,12 +552,13 @@ pub trait Provider: Send + Sync {
476552
self.stream_chat_with_system(system, last_user, model, temperature, options)
477553
}
478554

479-
/// Structured streaming chat interface.
555+
/// Structured streaming chat interface. See `simple_chat` for the
556+
/// `temperature` contract.
480557
fn stream_chat(
481558
&self,
482559
request: ChatRequest<'_>,
483560
model: &str,
484-
temperature: f64,
561+
temperature: Option<f64>,
485562
options: StreamOptions,
486563
) -> stream::BoxStream<'static, StreamResult<StreamEvent>> {
487564
self.stream_chat_with_history(request.messages, model, temperature, options)
@@ -500,6 +577,26 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
500577
self.as_ref().capabilities()
501578
}
502579

580+
fn default_temperature(&self) -> f64 {
581+
self.as_ref().default_temperature()
582+
}
583+
584+
fn default_max_tokens(&self) -> u32 {
585+
self.as_ref().default_max_tokens()
586+
}
587+
588+
fn default_timeout_secs(&self) -> u64 {
589+
self.as_ref().default_timeout_secs()
590+
}
591+
592+
fn default_base_url(&self) -> Option<&str> {
593+
self.as_ref().default_base_url()
594+
}
595+
596+
fn default_wire_api(&self) -> &str {
597+
self.as_ref().default_wire_api()
598+
}
599+
503600
fn convert_tools(&self, tools: &[ToolSpec]) -> ToolsPayload {
504601
self.as_ref().convert_tools(tools)
505602
}
@@ -517,7 +614,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
517614
system_prompt: Option<&str>,
518615
message: &str,
519616
model: &str,
520-
temperature: f64,
617+
temperature: Option<f64>,
521618
) -> anyhow::Result<String> {
522619
self.as_ref()
523620
.chat_with_system(system_prompt, message, model, temperature)
@@ -528,7 +625,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
528625
&self,
529626
messages: &[ChatMessage],
530627
model: &str,
531-
temperature: f64,
628+
temperature: Option<f64>,
532629
) -> anyhow::Result<String> {
533630
self.as_ref()
534631
.chat_with_history(messages, model, temperature)
@@ -539,7 +636,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
539636
&self,
540637
request: ChatRequest<'_>,
541638
model: &str,
542-
temperature: f64,
639+
temperature: Option<f64>,
543640
) -> anyhow::Result<ChatResponse> {
544641
self.as_ref().chat(request, model, temperature).await
545642
}
@@ -553,7 +650,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
553650
messages: &[ChatMessage],
554651
tools: &[serde_json::Value],
555652
model: &str,
556-
temperature: f64,
653+
temperature: Option<f64>,
557654
) -> anyhow::Result<ChatResponse> {
558655
self.as_ref()
559656
.chat_with_tools(messages, tools, model, temperature)
@@ -573,7 +670,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
573670
system_prompt: Option<&str>,
574671
message: &str,
575672
model: &str,
576-
temperature: f64,
673+
temperature: Option<f64>,
577674
options: StreamOptions,
578675
) -> stream::BoxStream<'static, StreamResult<StreamChunk>> {
579676
self.as_ref()
@@ -584,7 +681,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
584681
&self,
585682
messages: &[ChatMessage],
586683
model: &str,
587-
temperature: f64,
684+
temperature: Option<f64>,
588685
options: StreamOptions,
589686
) -> stream::BoxStream<'static, StreamResult<StreamChunk>> {
590687
self.as_ref()
@@ -595,7 +692,7 @@ impl<T: Provider + ?Sized> Provider for Arc<T> {
595692
&self,
596693
request: ChatRequest<'_>,
597694
model: &str,
598-
temperature: f64,
695+
temperature: Option<f64>,
599696
options: StreamOptions,
600697
) -> stream::BoxStream<'static, StreamResult<StreamEvent>> {
601698
self.as_ref()

0 commit comments

Comments
 (0)