rony-chat-bot/internal/persona/persona.go
Victor Hugo Vargas f33708534a feat: bootstrap rony-chat-bot Go module
Initial implementation of the bot:

- cmd/chat-bot: CLI entrypoint (serve, reindex, ask, version)
- internal/agent: LLM provider client + agent runner with RAG injection
- internal/config: YAML config loader (providers, RAG, persona, server)
- internal/i18n: response-language detection (EN/ES)
- internal/persona: persona system prompt assembly from YAML
- internal/portfolio: heading-based chunker + SQLite FTS5 indexer
- internal/server: chi router with /api/chat (SSE), /api/health, /api/info,
  /api/reindex, middleware (RequestID, Logging, CORS, RateLimit)
- internal/streaming: SSE protocol helpers (start, chunk, sources, done, error)
- web/: drop-in vanilla-JS chat widget (no build, no deps) + demo + README
- bench/: reproducible driver benchmark (modernc vs mattn SQLite)
- configs/portfolio-bot.yaml: llama.cpp default provider, SQLite RAG, canine persona
- docs/architecture.md / .es.md: aligned with SQLite FTS5 + llama.cpp decisions
- data/projects/README*.md: project data documentation
- README.md / .es.md: updated for current implementation

All tests pass (go test ./...). Bot is functional end-to-end with the
configured LLM provider.
2026-07-17 00:56:06 -07:00

44 lines
No EOL
1.6 KiB
Go

// Package persona owns the bot's identity and the system prompt the LLM
// sees. The full prompt text lives in the YAML's `system_prompt` field
// (long, freeform, hand-tuned for the deployment). This package only
// adds the RAG context after it.
package persona
import (
"github.com/VictorVargas/rony-llm-agent/pkg/persona"
"github.com/VictorVargas/rony-chat-bot/internal/config"
)
// FromConfig returns a minimal persona used only for the UI greeting and
// the SSE event metadata. The system prompt itself comes from
// config.SystemPrompt, not from this struct — see BuildSystemPrompt.
func FromConfig(c *config.Config) (persona.Persona, error) {
lang := c.Persona.Language
if lang == "" {
lang = "the user's language"
}
return persona.Persona{
ID: "rony",
Name: c.Persona.Name,
Tone: c.Persona.Tone,
Language: lang,
}, nil
}
// BuildSystemPrompt returns the full prompt for one chat turn:
// 1. The hand-written system prompt from the YAML (who Rony is, how to speak)
// 2. The RAG block (omitted when the index returns no hits)
//
// The RAG block is appended, not prepended, so the persona instructions
// always come first and the LLM never gets the chance to "forget" them.
func BuildSystemPrompt(systemPrompt, ragContext string) string {
out := systemPrompt
if ragContext != "" {
out += "\n\n## Relevant context from the portfolio\n\n" +
"Use these excerpts to answer. Cite the project filename when you reference a detail. " +
"If the excerpts don't contain the answer, say you don't have that information — do not invent.\n\n" +
ragContext
}
return out
}