1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! A minimal text-only chatbot built on the [`Agent`] SDK — no audio, no
//! models, no TTS. Point it at any OpenAI-compatible LLM server (local Ollama
//! by default) and chat on stdin/stdout.
//!
//! # Setup
//!
//! 1. Install and start Ollama (an OpenAI-compatible local server):
//!
//! ```sh
//! curl -fsSL https://ollama.com/install.sh | sh
//! ollama serve # listens on http://localhost:11434
//! # download + register the default model (one-time):
//! wget https://huggingface.co/StealthyML/StealthyLM-Emotive/resolve/main/StealthyLM_Q4KM.gguf
//! echo 'FROM ./StealthyLM_Q4KM.gguf' > Modelfile
//! ollama create stealthylm -f Modelfile
//! ```
//!
//! 2. Build and run the chatbot:
//!
//! ```sh
//! cargo run --example chatbot
//! ```
//!
//! 3. Chat away. Type `/quit` (or press ctrl-d) to exit, and `/clear` to wipe
//! the conversation history and start fresh.
//!
//! # Configuration (environment variables)
//!
//! | Variable | Default | Meaning |
//! |---------------------|-----------------------------|----------------------------------|
//! | `SKADOOSH_BASE_URL` | `http://localhost:11434/v1` | OpenAI-compatible LLM base URL |
//! | `SKADOOSH_MODEL` | `stealthylm` | Model name for chat completions |
//! | `SKADOOSH_API_KEY` | `ollama` | Bearer token (Ollama needs none) |
//!
//! Any hosted OpenAI-compatible provider works too — set the base URL, model,
//! and API key to point at it.
//!
//! # How it works
//!
//! The crate ships no `HttpLlm` type, so (like the `text_chat` example) this
//! uses the config-based [`LlmClient`]: [`Agent::builder`] assembles a
//! text-output agent ([`OutputMode::Text`] → no TTS, no playback), and the LLM
//! client is built lazily from the [`Config`] on the first
//! [`Agent::text_turn`]. Each input line is one `text_turn`; the full reply
//! text is returned and printed as `Bot> {reply}`. Conversation history
//! accumulates across turns (bounded by `Config::max_history_turns`); `/clear`
//! rebuilds the agent from the same config, resetting history to just the
//! system prompt.
//!
//! The REPL core ([`run_repl`]) is split out of [`main`] so
//! `tests/examples.rs` can drive it in-memory against a scripted LLM backend —
//! no server, no stdin/stdout — using exactly the same `Agent::text_turn`
//! path.
use ;
use ;
/// OpenAI-compatible base URL (env `SKADOOSH_BASE_URL`).
const DEFAULT_BASE_URL: &str = "http://localhost:11434/v1";
/// Model name (env `SKADOOSH_MODEL`).
const DEFAULT_MODEL: &str = "stealthylm";
/// Bearer token / API key (env `SKADOOSH_API_KEY`).
const DEFAULT_API_KEY: &str = "ollama";
/// Builds a text-output [`Config`] from the `SKADOOSH_*` environment
/// variables, falling back to the Ollama defaults above.
/// Builds a fresh [`Agent`] from the env-derived config. A brand-new agent
/// starts with just the seeded system prompt, so calling this again on
/// `/clear` resets the conversation history.
/// Wraps a std I/O error as a [`SkadooshError`] (via `anyhow`), matching the
/// crate's own repl I/O-error handling — `SkadooshError` has no direct
/// `From<std::io::Error>`, so the `?` operator needs this bridge.
/// The chatbot REPL core, split from [`main`] so tests can drive it in-memory
/// (against a scripted LLM, no server) without stdin/stdout.
///
/// Prints `You> ` before reading each line from `input` and `Bot> {reply}`
/// after each [`Agent::text_turn`]. `make_agent` builds the initial agent and
/// is called again on `/clear` to reset history. `/quit` or EOF exits; LLM
/// errors are printed and the loop continues (I/O errors propagate, since a
/// broken stdin/stdout is terminal).