feat(kei-buddy): scaffold runtime crate — 11-state onboarding FSM enum
First atom of the kei-buddy phase-1 plan. Pure scaffold — no business
logic; that comes in follow-up commits.
Crate location: _primitives/_rust/kei-buddy/
LOC: 262 across 7 files (largest src/state.rs 85 LOC; all <200).
Contents:
* src/state.rs — OnboardState enum with 11 variants matching the
TS state-machine in keisei-marketplace/src/lib/keibuddy/chat-onboard.ts:
Intro, AskName, AskTone, AskInterests, AskHobbies, TopicSpecifics,
TopicNowLater, TopicResearch, TopicSources, AskSchedule, Ready.
serde(rename_all = "snake_case") matches TS naming.
`next()` is a stub (returns self.clone(); real transitions TBD).
* src/transition.rs — TransitionInput struct (user_text +
extracted_fields json::Value). Struct only, no extraction yet.
* src/error.rs — BuddyError enum via thiserror (StateMachine /
Memory / Transport). No From impls yet.
* src/lib.rs — module declarations + re-exports.
* src/bin/kei-buddy.rs — minimal `kei-buddy serve` clap subcommand,
currently prints "not yet implemented".
* Cargo.toml — workspace member, maturity = "concept".
* README.md — crate-level README, roadmap of 4 follow-up bullets.
Workspace registration: _primitives/_rust/Cargo.toml members list
gains "kei-buddy". Lockfile updated accordingly.
Verify-before-commit (RULE 0.13 §):
* cargo check --offline -p kei-buddy: PASS
* cargo test --offline -p kei-buddy --lib: 1 passed / 0 failed
(state::tests::all_variants_serde_roundtrip)
* cargo check --workspace --offline: PASS
* STATUS-TRUTH MARKER from agent: shipped=scaffolding, stubs=1
(state.rs:50 next() returns self.clone(), expected for scaffold)
Follow-up tasks (tracked in TaskList):
* Port handleStep transition logic from chat-onboard.ts
* LLM extract via kei-cortex
* Memory binding via kei-memory-sqlite
* Telegram webhook driver (new crate kei-telegram-webhook)
* kei-tts trait + 4 backends (ElevenLabs / OpenAI / Google / Piper)
* kei-stt trait + 3 backends (Whisper local / Deepgram / OpenAI API)
This commit is contained in:
parent
94e975c92b
commit
a2d4bc9206
9 changed files with 304 additions and 29 deletions
69
_primitives/_rust/Cargo.lock
generated
69
_primitives/_rust/Cargo.lock
generated
|
|
@ -713,7 +713,7 @@ dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.4",
|
"tokio-rustls 0.26.4",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -889,7 +889,7 @@ dependencies = [
|
||||||
"sync_wrapper 1.0.2",
|
"sync_wrapper 1.0.2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite 0.24.0",
|
"tokio-tungstenite 0.24.0",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -1574,12 +1574,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "6.1.0"
|
version = "5.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
"lock_api",
|
"lock_api",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
|
@ -3186,6 +3185,18 @@ dependencies = [
|
||||||
"thiserror 1.0.69",
|
"thiserror 1.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kei-buddy"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kei-cache"
|
name = "kei-cache"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -3366,7 +3377,7 @@ dependencies = [
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml",
|
"toml",
|
||||||
"tower",
|
"tower 0.4.13",
|
||||||
"tower-http 0.5.2",
|
"tower-http 0.5.2",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|
@ -3530,7 +3541,7 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower 0.4.13",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
@ -3683,22 +3694,6 @@ dependencies = [
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kei-graph-stream"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"axum",
|
|
||||||
"clap",
|
|
||||||
"futures",
|
|
||||||
"reqwest 0.12.28",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
|
||||||
"tokio-tungstenite 0.29.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kei-hibernate"
|
name = "kei-hibernate"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
@ -4500,11 +4495,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kqueue-sys"
|
name = "kqueue-sys"
|
||||||
version = "1.0.4"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b"
|
checksum = "a7b65860415f949f23fa882e669f2dbd4a0f0eeb1acdd56790b30494afd7da2f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 2.11.1",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -5894,7 +5889,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.4",
|
"tokio-rustls 0.26.4",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-http 0.6.8",
|
"tower-http 0.6.8",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
|
|
@ -7546,6 +7541,23 @@ version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"pin-project",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
|
@ -7557,7 +7569,6 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"sync_wrapper 1.0.2",
|
"sync_wrapper 1.0.2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -7593,7 +7604,7 @@ dependencies = [
|
||||||
"http-body 1.0.1",
|
"http-body 1.0.1",
|
||||||
"iri-string",
|
"iri-string",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tower",
|
"tower 0.5.3",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,8 @@ members = [
|
||||||
"kei-db-contract",
|
"kei-db-contract",
|
||||||
# Live runtime-graph exporter (registry + ledger → D3 space fragment)
|
# Live runtime-graph exporter (registry + ledger → D3 space fragment)
|
||||||
"kei-graph-export",
|
"kei-graph-export",
|
||||||
|
# KeiBuddy personal-assistant Telegram bot — onboarding FSM scaffold
|
||||||
|
"kei-buddy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|
|
||||||
33
_primitives/_rust/kei-buddy/Cargo.toml
Normal file
33
_primitives/_rust/kei-buddy/Cargo.toml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
[package]
|
||||||
|
name = "kei-buddy"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
description = "KeiBuddy personal-assistant Telegram bot — onboarding state-machine + skeleton driver. Concept-level scaffold."
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "kei-buddy"
|
||||||
|
path = "src/bin/kei-buddy.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "kei_buddy"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
|
||||||
|
tracing = "0.1"
|
||||||
|
clap = { workspace = true, features = ["derive"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# future: pulls in kei-notify-telegram for real Telegram transport
|
||||||
|
telegram = []
|
||||||
|
|
||||||
|
[package.metadata.keisei]
|
||||||
|
maturity = "concept"
|
||||||
|
description = "KeiBuddy personal-assistant: onboarding FSM + bot driver scaffold"
|
||||||
|
authors = ["Denis Parfionovich <parfionovich@keilab.io>"]
|
||||||
37
_primitives/_rust/kei-buddy/README.md
Normal file
37
_primitives/_rust/kei-buddy/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# kei-buddy
|
||||||
|
|
||||||
|
**Maturity:** concept / scaffold — no business logic yet.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
`kei-buddy` is the runtime crate that composes existing KeiSeiKit
|
||||||
|
primitives (`kei-pet`, `kei-memory-sqlite`, `kei-cortex`,
|
||||||
|
`kei-notify-telegram`) into a personal-assistant Telegram bot called
|
||||||
|
KeiBuddy.
|
||||||
|
|
||||||
|
On first contact the bot walks the user through an 11-state onboarding
|
||||||
|
flow: name, tone, interests, hobbies, per-topic decomposition (specifics
|
||||||
|
→ now-or-later → research preference → source selection), and digest
|
||||||
|
schedule. After onboarding the bot enters ongoing conversation mode,
|
||||||
|
drawing on the stored persona and memory.
|
||||||
|
|
||||||
|
This crate provides the state-machine enum and skeleton driver. The
|
||||||
|
onboarding FSM is ported from
|
||||||
|
`keisei-marketplace/src/lib/keibuddy/chat-onboard.ts`.
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Scaffold only. The `OnboardState` enum and `TransitionInput` struct are
|
||||||
|
defined. All transition logic is stubbed (`next()` returns `self.clone()`).
|
||||||
|
The binary entry point prints a placeholder message and exits 0.
|
||||||
|
|
||||||
|
## Roadmap
|
||||||
|
|
||||||
|
- **State-machine transition logic** — port `handleStep` from
|
||||||
|
`chat-onboard.ts`; wire per-state LLM-extract calls through kei-cortex.
|
||||||
|
- **Memory binding** — persist scratchpad and finalised persona via
|
||||||
|
kei-memory-sqlite; implement `getChatState` / `setChatStep` equivalents.
|
||||||
|
- **Persona binding** — read persona manifest via `kei-pet`; apply tone
|
||||||
|
overlay to outgoing replies.
|
||||||
|
- **Transport binding** — wire kei-notify-telegram for outbound messages;
|
||||||
|
add a real Telegram webhook server (or kei-gateway adapter) for inbound.
|
||||||
34
_primitives/_rust/kei-buddy/src/bin/kei-buddy.rs
Normal file
34
_primitives/_rust/kei-buddy/src/bin/kei-buddy.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//! kei-buddy binary entry point.
|
||||||
|
//!
|
||||||
|
//! Scaffold — the `serve` subcommand is a no-op stub until the
|
||||||
|
//! Telegram webhook driver and memory layer are wired in.
|
||||||
|
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(
|
||||||
|
name = "kei-buddy",
|
||||||
|
about = "KeiBuddy personal-assistant bot (scaffold)",
|
||||||
|
version
|
||||||
|
)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Command {
|
||||||
|
/// Start the Telegram webhook listener (not yet implemented).
|
||||||
|
Serve,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
match cli.command {
|
||||||
|
Command::Serve => {
|
||||||
|
println!("kei-buddy serve: not yet implemented, scaffold only");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
_primitives/_rust/kei-buddy/src/error.rs
Normal file
26
_primitives/_rust/kei-buddy/src/error.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//! Error type for kei-buddy operations.
|
||||||
|
//!
|
||||||
|
//! Three categories cover the three integration layers that will be
|
||||||
|
//! wired in follow-up tasks: state-machine, memory store, transport.
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Top-level error type for the KeiBuddy crate.
|
||||||
|
///
|
||||||
|
/// Variants will gain `#[from]` impls once the concrete dependencies
|
||||||
|
/// (kei-memory-sqlite, kei-notify-telegram, kei-cortex) are wired.
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum BuddyError {
|
||||||
|
/// Error originating in the onboarding state machine.
|
||||||
|
#[error("state machine error: {0}")]
|
||||||
|
StateMachine(String),
|
||||||
|
|
||||||
|
/// Error originating in the memory persistence layer.
|
||||||
|
#[error("memory error: {0}")]
|
||||||
|
Memory(String),
|
||||||
|
|
||||||
|
/// Error originating in the Telegram (or other) transport layer.
|
||||||
|
#[error("transport error: {0}")]
|
||||||
|
Transport(String),
|
||||||
|
}
|
||||||
23
_primitives/_rust/kei-buddy/src/lib.rs
Normal file
23
_primitives/_rust/kei-buddy/src/lib.rs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//! kei-buddy — KeiBuddy personal-assistant Telegram bot scaffold.
|
||||||
|
//!
|
||||||
|
//! Concept-level crate. This file declares the public module surface.
|
||||||
|
//! No business logic lives here; see individual modules.
|
||||||
|
//!
|
||||||
|
//! Module layout (Constructor Pattern — one file, one responsibility):
|
||||||
|
//! * `state` — `OnboardState` enum + `next()` stub
|
||||||
|
//! * `transition` — `TransitionInput` input struct
|
||||||
|
//! * `error` — `BuddyError` error type
|
||||||
|
//!
|
||||||
|
//! Follow-up tasks will add:
|
||||||
|
//! * LLM extraction via kei-cortex
|
||||||
|
//! * Memory persistence via kei-memory-sqlite
|
||||||
|
//! * Telegram webhook driver (kei-notify-telegram)
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub mod state;
|
||||||
|
pub mod transition;
|
||||||
|
|
||||||
|
pub use error::BuddyError;
|
||||||
|
pub use state::OnboardState;
|
||||||
|
pub use transition::TransitionInput;
|
||||||
85
_primitives/_rust/kei-buddy/src/state.rs
Normal file
85
_primitives/_rust/kei-buddy/src/state.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//! Onboarding state-machine enum.
|
||||||
|
//!
|
||||||
|
//! Ported from `keisei-marketplace/src/lib/keibuddy/chat-onboard.ts`.
|
||||||
|
//! Each variant corresponds to one `Step` in the TypeScript source.
|
||||||
|
//! Transitions are stubs; real logic arrives in a follow-up task.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::transition::TransitionInput;
|
||||||
|
|
||||||
|
/// 11-state onboarding finite-state machine.
|
||||||
|
///
|
||||||
|
/// Mirrors the TypeScript `Step` union type exactly:
|
||||||
|
/// `intro | ask_name | ask_tone | ask_interests | ask_hobbies |
|
||||||
|
/// topic_specifics | topic_now_later | topic_research |
|
||||||
|
/// topic_sources | ask_schedule | ready`
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum OnboardState {
|
||||||
|
/// Initial greeting — bot explains itself.
|
||||||
|
Intro,
|
||||||
|
/// Collecting user's display name.
|
||||||
|
AskName,
|
||||||
|
/// Collecting preferred communication tone.
|
||||||
|
AskTone,
|
||||||
|
/// Collecting list of interests.
|
||||||
|
AskInterests,
|
||||||
|
/// Collecting list of hobbies.
|
||||||
|
AskHobbies,
|
||||||
|
/// Per-topic: "what specifically interests you here?"
|
||||||
|
TopicSpecifics,
|
||||||
|
/// Per-topic: "discuss now or save for later?"
|
||||||
|
TopicNowLater,
|
||||||
|
/// Per-topic: "want ongoing source monitoring?"
|
||||||
|
TopicResearch,
|
||||||
|
/// Per-topic: "here are proposed sources, which to add?"
|
||||||
|
TopicSources,
|
||||||
|
/// Collecting digest schedule (morning/evening hours + timezone).
|
||||||
|
AskSchedule,
|
||||||
|
/// Onboarding complete; regular conversation mode.
|
||||||
|
Ready,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnboardState {
|
||||||
|
/// Advance to the next state given user input.
|
||||||
|
///
|
||||||
|
/// **Stub** — returns `self.clone()` until transition logic is ported.
|
||||||
|
/// Real implementation will extract fields via kei-cortex and follow
|
||||||
|
/// the per-topic queue logic from `chat-onboard.ts::handleStep`.
|
||||||
|
pub fn next(&self, _input: &TransitionInput) -> Self {
|
||||||
|
// TODO: port transition logic from chat-onboard.ts::handleStep
|
||||||
|
self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Smoke test: every variant round-trips through JSON serialisation.
|
||||||
|
#[test]
|
||||||
|
fn all_variants_serde_roundtrip() {
|
||||||
|
let variants = [
|
||||||
|
OnboardState::Intro,
|
||||||
|
OnboardState::AskName,
|
||||||
|
OnboardState::AskTone,
|
||||||
|
OnboardState::AskInterests,
|
||||||
|
OnboardState::AskHobbies,
|
||||||
|
OnboardState::TopicSpecifics,
|
||||||
|
OnboardState::TopicNowLater,
|
||||||
|
OnboardState::TopicResearch,
|
||||||
|
OnboardState::TopicSources,
|
||||||
|
OnboardState::AskSchedule,
|
||||||
|
OnboardState::Ready,
|
||||||
|
];
|
||||||
|
for variant in &variants {
|
||||||
|
let json = serde_json::to_string(variant)
|
||||||
|
.unwrap_or_else(|e| panic!("serialize {:?}: {e}", variant));
|
||||||
|
let back: OnboardState = serde_json::from_str(&json)
|
||||||
|
.unwrap_or_else(|e| panic!("deserialize {:?} from {json:?}: {e}", variant));
|
||||||
|
assert_eq!(variant, &back, "round-trip failed for {:?}", variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
_primitives/_rust/kei-buddy/src/transition.rs
Normal file
24
_primitives/_rust/kei-buddy/src/transition.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//! Input struct for state-machine transitions.
|
||||||
|
//!
|
||||||
|
//! `TransitionInput` carries the raw user message and any structured
|
||||||
|
//! fields that an LLM extractor has already parsed from it.
|
||||||
|
//! In the scaffold phase the `extracted_fields` value is always `null`.
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Input to `OnboardState::next()`.
|
||||||
|
///
|
||||||
|
/// `user_text` is the verbatim Telegram message body.
|
||||||
|
/// `extracted_fields` will hold the result of an LLM-extract call
|
||||||
|
/// (e.g. `extractName`, `extractTone`, `extractList` from
|
||||||
|
/// `chat-onboard-extract.ts`) once kei-cortex integration is wired.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TransitionInput {
|
||||||
|
/// Raw message text from the user, UTF-8, already trimmed.
|
||||||
|
pub user_text: String,
|
||||||
|
|
||||||
|
/// Structured fields extracted by an LLM call.
|
||||||
|
/// `serde_json::Value::Null` until kei-cortex integration is added.
|
||||||
|
pub extracted_fields: serde_json::Value,
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue