From 0778e39dec234006fd17faa25fd51604d1ee97c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 20:10:45 +0000 Subject: [PATCH 1/2] Support local models via OPENAI_BASE_URL + OPENAI_API_KEY Self-hosters can now run Mu with local models (Ollama, llama.cpp, vLLM, LocalAI) by setting OPENAI_BASE_URL to their local endpoint. Uses the same OpenAI-compatible protocol as Atlas Cloud. Example with Ollama: OPENAI_BASE_URL=http://localhost:11434/v1 OPENAI_API_KEY=ollama Falls back to Atlas Cloud (api.atlascloud.ai) when OPENAI_BASE_URL is not set. OPENAI_API_KEY is accepted as an alias for ATLAS_API_KEY. --- internal/ai/providers.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/ai/providers.go b/internal/ai/providers.go index f18fddfa..421be11f 100644 --- a/internal/ai/providers.go +++ b/internal/ai/providers.go @@ -28,9 +28,20 @@ var ( cacheReadTokens int cacheCreationTokens int - // Atlas Cloud config - atlasAPIKey = os.Getenv("ATLAS_API_KEY") - atlasBaseURL = "https://api.atlascloud.ai/v1" + // Atlas Cloud / OpenAI-compatible config. + // Set OPENAI_BASE_URL to use a local model server (Ollama, llama.cpp, etc.) + atlasAPIKey = func() string { + if v := os.Getenv("ATLAS_API_KEY"); v != "" { + return v + } + return os.Getenv("OPENAI_API_KEY") + }() + atlasBaseURL = func() string { + if v := os.Getenv("OPENAI_BASE_URL"); v != "" { + return strings.TrimRight(v, "/") + } + return "https://api.atlascloud.ai/v1" + }() ) // Atlas Cloud model aliases — used to route requests to Atlas Cloud From 7133f1f06804d45059f38db4617742dd6ad512d6 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 5 Jun 2026 20:55:37 +0000 Subject: [PATCH 2/2] =?UTF-8?q?Shift=20to=20personal=20AI=20platform=20?= =?UTF-8?q?=E2=80=94=20prompt-first,=20user=20context,=20new=20framing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three changes toward the AI-first vision: 1. Prompt is now the primary interface: - Moved above the date header — first thing on screen - Larger: 16px font, 14px padding, 14px border radius - Placeholder: "What do you need?" (was "Search or look up...") - Cards scroll below, prompt stays prominent 2. Agent gets user context: - UserContextFunc callback wired in main.go - Injects unread mail count and wallet balance into the synthesis prompt so the AI knows the user's state - Agent introduces itself as "Micro, a personal AI assistant" instead of "a helpful assistant" - Easily extensible — add market watchlist, reading history, preferences as the data model grows 3. Framing: - README: "Your personal AI" not "The everything app" - "Ask it anything — it checks your mail, looks up prices, searches the web, reads the news" - Product is now positioned as an AI that knows you and can do things for you, not a dashboard with AI bolted on https://claude.ai/code/session_01GRGLA9yj7BpqKiyi6xFwnm --- README.md | 7 ++++--- agent/run.go | 20 ++++++++++++++++---- home/home.go | 14 +++++++------- main.go | 18 ++++++++++++++++++ 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f50a4ae8..e0ccef86 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # mu -A personal app platform — blog, chat, news, mail, video and more. +Your personal AI — news, mail, markets, weather, search and more, all through one interface. No ads. No tracking. No algorithm. ## Overview -The current tech ecosystem is totally broken. All the app platform are filled with ads and addictive content. I'm tired of it. You probably are too. -So here's a personal app platform. Blog, chat, news, mail, video and more. No ads, no tracking, no algorithms. Built in the open. It's called Mu for short. +Mu is a personal AI platform. Ask it anything — it checks your mail, looks up prices, searches the web, reads the news, and gives you a personalised answer. Every service is a tool the AI can use on your behalf. + +Built in the open. Pay for the tools, not with your attention. ### What's included diff --git a/agent/run.go b/agent/run.go index b511bc5b..bcb4f281 100644 --- a/agent/run.go +++ b/agent/run.go @@ -13,7 +13,11 @@ import ( "mu/internal/auth" ) -// RunRequest is the input for the synchronous agent endpoint. +// UserContextFunc is set by main.go to provide personalised context +// for the agent's responses. Returns a string with the user's current +// state (unread mail, market prices, etc.) that gets injected into the +// synthesis prompt. +var UserContextFunc func(accountID string) string type RunRequest struct { Prompt string `json:"prompt"` Model string `json:"model"` @@ -130,11 +134,19 @@ func RunHandler(w http.ResponseWriter, r *http.Request) { toolsUsed = append(toolsUsed, ToolUsed{Name: tc.Tool, Status: "ok"}) } - // Step 3: Synthesise + // Step 3: Synthesise with user context. today := time.Now().UTC().Format("Monday, 2 January 2006 (UTC)") + userCtx := "" + if UserContextFunc != nil { + userCtx = UserContextFunc(acc.ID) + } + synthSystem := "You are Micro, a personal AI assistant. Today is " + today + ". " + + "Answer concisely using the tool results below. Use markdown." + if userCtx != "" { + synthSystem += "\n\nUser context:\n" + userCtx + } answer, err := ai.Ask(&ai.Prompt{ - System: "You are a helpful assistant. Today's date is " + today + ". " + - "Answer using ONLY the tool results below. Use markdown.", + System: synthSystem, Rag: ragParts, Question: req.Prompt, Priority: ai.PriorityHigh, diff --git a/home/home.go b/home/home.go index c5cfd399..4d2f925c 100644 --- a/home/home.go +++ b/home/home.go @@ -446,22 +446,22 @@ function fetchW(la,lo){ // ── Cards (always visible) ── b.WriteString(`
`) - b.WriteString(dateHTML) - // Console prompt — inline at the top, before cards. Claude-style - // rounded textarea with send button inside. + // AI prompt — the primary interface. First thing on screen. if viewerID != "" { b.WriteString(fmt.Sprintf(` -
+
- - + +
- +
`, stream.MaxContentLength)) b.WriteString(consoleScript) } + b.WriteString(dateHTML) + // Inline card preferences panel if viewerAcc != nil { allCardDefs := []struct{ id, label string }{ diff --git a/main.go b/main.go index ad11219b..b55e47ad 100644 --- a/main.go +++ b/main.go @@ -119,6 +119,24 @@ func main() { // load agent agent.Load() + // Wire user context into the agent — personalises responses. + agent.UserContextFunc = func(accountID string) string { + var parts []string + // Unread mail count. + if unread := mail.GetUnreadCount(accountID); unread > 0 { + parts = append(parts, fmt.Sprintf("- %d unread email(s)", unread)) + } + // Wallet balance. + bal := wallet.GetBalance(accountID) + if bal > 0 { + parts = append(parts, fmt.Sprintf("- Wallet: %d credits", bal)) + } + if len(parts) == 0 { + return "" + } + return strings.Join(parts, "\n") + } + // Wire digest → blog callbacks (digest publishes as blog post) digest.PublishBlogPost = func(title, content, author, authorID, tags string) (string, error) { err := blog.CreatePost(title, content, author, authorID, tags, false)