feat: {response,mood} output contract + router removal, TTS piper plan

Daemon side of Decision B: parse {"response","mood"} across the 4 consumers
(replier, nudges, reminders, chat), fall back to legacy formats. Drop the
LLM router — the classifier handles routing; replier/phraser share one
llm.Client (timeout 20s->60s). llm.Client reads reasoning_content when
content is empty (thinking models).

Docs: TTS piper-student plan (OmniVoice teacher -> piper student, from
scratch, phoneme-first). CLAUDE.md training guide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kami
2026-07-11 22:51:50 +04:00
parent 22b43c07a9
commit 6a5121657a
13 changed files with 952 additions and 51 deletions
+10 -4
View File
@@ -35,8 +35,10 @@ type Req struct {
}
type msg struct {
Role string `json:"role"`
Content string `json:"content"`
Role string `json:"role"`
Content string `json:"content"`
Reasoning string `json:"reasoning,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
}
type body struct {
Messages []msg `json:"messages"`
@@ -54,7 +56,7 @@ type resp struct {
func (c *Client) Complete(ctx context.Context, r Req) (string, error) {
b, _ := json.Marshal(body{
Messages: []msg{{"system", r.System}, {"user", r.User}},
Messages: []msg{{Role: "system", Content: r.System}, {Role: "user", Content: r.User}},
MaxTokens: r.MaxTokens,
Grammar: r.Grammar,
Temp: 0,
@@ -81,5 +83,9 @@ func (c *Client) Complete(ctx context.Context, r Req) (string, error) {
if len(out.Choices) == 0 {
return "", fmt.Errorf("llm: no choices")
}
return out.Choices[0].Message.Content, nil
content := out.Choices[0].Message.Content
if content == "" {
content = out.Choices[0].Message.ReasoningContent
}
return content, nil
}