Commit f5971679ab17

Vincent Demeester <vincent@sbr.pm>
2026-01-14 19:51:39
feat(xmpp-research-bot): add Gemini 3 Pro support and configurable save locations
Major improvements: ## 1. Gemini 3 Pro Support - Added Gemini 3 Pro (gemini-3-pro-preview) support - Model selection: gemini-pro: or gp: prefix - Uses HIGH thinking level for advanced reasoning - Fixed package name: google-genai (was google-generativeai) Available Gemini models: - Gemini 3 Flash (gemini:, g:) - Fast, cost-effective - Gemini 3 Pro (gemini-pro:, gp:) - Advanced reasoning, 1M context ## 2. Configurable Save Locations Changed save_to_inbox to save_to with flexible options: - save_to: inbox.org - Save to inbox.org (relative path) - save_to: research.org - Save to custom file - save_to: /path/to/file.org - Absolute path - save_to: false - Don't save, just reply ## 3. Dotfiles Configuration Created /home/vincent/.config/xmpp-research-bot/commands.yaml: - Includes 5 default commands: research, summarize, analyze, eli5, translate - Uses new save_to format - Wired into aomi config with commandsPath - Hot-reloadable with /reload-commands Example commands.yaml shows different model choices: - research: sonnet (default, fast) - analyze: gemini-pro (deep reasoning) - eli5/translate: gemini or sonnet (fast) ## Model Lineup Summary Claude: - Opus 4.5: Most intelligent, deep analysis - Sonnet 4.5: Fast, cost-effective (default) Gemini: - Gemini 3 Pro: Advanced reasoning, 1M context - Gemini 3 Flash: Fastest, cheapest Usage: /research how does XMPP work? # Sonnet (default) /research opus: complex analysis # Opus 4.5 /research gp: solve reasoning problem # Gemini 3 Pro /research g: quick summary # Gemini 3 Flash Sources: - Gemini 3 Pro: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/models/gemini/3-pro - Google Gen AI SDK: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/sdks/overview - Thinking levels: https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thinking Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 0121a76
Changed files (5)
dots
config
xmpp-research-bot
modules
systems
dots/config/xmpp-research-bot/commands.yaml
@@ -0,0 +1,93 @@
+# XMPP Research Bot Commands Configuration
+#
+# This file defines custom commands for the research bot.
+# After editing, use /reload-commands in XMPP to apply changes.
+
+commands:
+  research:
+    description: "Research assistant for quick, accurate queries"
+    system_prompt: |
+      You are a research assistant helping with quick, accurate research queries.
+
+      Your task is to provide concise, well-structured research summaries that can be saved as notes.
+
+      Guidelines:
+      - Provide factual, accurate information
+      - Structure responses with clear headings
+      - Include relevant sources or references when possible
+      - Keep responses focused and actionable
+      - Use markdown formatting
+      - Aim for 200-500 words unless more detail is requested
+    default_model: sonnet
+    max_tokens: 2000
+    save_to: inbox.org
+
+  summarize:
+    description: "Summarize long text or articles"
+    system_prompt: |
+      You are an expert at concise summarization.
+
+      Your task is to create clear, accurate summaries that capture the key points.
+
+      Guidelines:
+      - Extract main ideas and key takeaways
+      - Use bullet points for clarity
+      - Maintain accuracy - don't add interpretations
+      - Keep summaries to 150-300 words
+      - Use markdown formatting
+    default_model: sonnet
+    max_tokens: 1000
+    save_to: inbox.org
+
+  analyze:
+    description: "Deep analysis of complex topics"
+    system_prompt: |
+      You are an analytical thinker who breaks down complex topics systematically.
+
+      Your task is to provide in-depth analysis with multiple perspectives.
+
+      Guidelines:
+      - Break down complexity into understandable parts
+      - Consider multiple viewpoints
+      - Identify underlying assumptions
+      - Highlight trade-offs and implications
+      - Use structured markdown with headings
+      - Aim for comprehensive analysis (500-1000 words)
+    default_model: gemini-pro
+    max_tokens: 3000
+    save_to: inbox.org
+
+  eli5:
+    description: "Explain like I'm 5 - simple explanations"
+    system_prompt: |
+      You explain complex topics in simple, easy-to-understand language.
+
+      Your task is to make complicated ideas accessible to anyone.
+
+      Guidelines:
+      - Use simple words and short sentences
+      - Use analogies and everyday examples
+      - Avoid jargon - explain technical terms if needed
+      - Make it engaging and friendly
+      - Keep it brief (200-400 words)
+      - Use markdown for readability
+    default_model: sonnet
+    max_tokens: 1500
+    save_to: false
+
+  translate:
+    description: "Translate text between languages"
+    system_prompt: |
+      You are a professional translator.
+
+      Your task is to provide accurate, natural-sounding translations.
+
+      Guidelines:
+      - Preserve meaning and tone
+      - Use natural phrasing in target language
+      - Note any cultural adaptations needed
+      - If source language isn't specified, detect it
+      - Format: "From [language] to [language]:\n\n[translation]"
+    default_model: gemini
+    max_tokens: 2000
+    save_to: false
modules/xmpp-research-bot/bot.py
@@ -47,7 +47,7 @@ Guidelines:
 - Aim for 200-500 words unless more detail is requested""",
         "default_model": "sonnet",
         "max_tokens": 2000,
-        "save_to_inbox": True,
+        "save_to": "inbox.org",
     }
 }
 
@@ -165,10 +165,12 @@ class ResearchBot(slixmpp.ClientXMPP):
                 help_lines.append("    opus: or o: - Use Claude Opus 4.5 (most intelligent)")
                 help_lines.append("    sonnet: or s: - Use Claude Sonnet 4.5 (default, faster)")
                 if self.gemini_client:
+                    help_lines.append("    gemini-pro: or gp: - Use Gemini 3 Pro (advanced reasoning)")
                     help_lines.append("    gemini: or g: - Use Gemini 3 Flash (fast, cost-effective)")
                 help_lines.append("  Examples:")
                 help_lines.append("    /research how does XMPP work?")
                 help_lines.append("    /research opus: analyze distributed systems complexity")
+                help_lines.append("    /research gp: solve complex reasoning problem")
 
         # Built-in commands
         help_lines.append("/help - Show this help message")
@@ -218,6 +220,13 @@ class ResearchBot(slixmpp.ClientXMPP):
             model = "gemini"
             model_name = "Gemini 3 Flash"
             query = query.split(":", 1)[1].strip()
+        elif query.startswith("gemini-pro:") or query.startswith("gp:"):
+            if not self.gemini_client:
+                msg.reply("โŒ Gemini is not available (client not initialized)").send()
+                return
+            model = "gemini-pro"
+            model_name = "Gemini 3 Pro"
+            query = query.split(":", 1)[1].strip()
 
         # Acknowledge we picked up the work
         msg.reply(f"๐Ÿ” {cmd_config.get('description', 'Processing')} with {model_name}: {query}").send()
@@ -231,10 +240,11 @@ class ResearchBot(slixmpp.ClientXMPP):
                 max_tokens=cmd_config.get('max_tokens', 2000)
             )
 
-            # Save to inbox if configured
-            if cmd_config.get('save_to_inbox', True):
-                await self.save_to_inbox(query, result, model=model_name)
-                msg.reply(f"โœ… Complete! Saved to inbox.org\n\nPreview:\n{result[:200]}...").send()
+            # Save to file if configured
+            save_to = cmd_config.get('save_to')
+            if save_to:
+                await self.save_to_file(query, result, filename=save_to, model=model_name)
+                msg.reply(f"โœ… Complete! Saved to {save_to}\n\nPreview:\n{result[:200]}...").send()
             else:
                 msg.reply(f"โœ… Complete!\n\n{result}").send()
 
@@ -250,6 +260,7 @@ class ResearchBot(slixmpp.ClientXMPP):
             "sonnet": "Sonnet 4.5",
             "opus": "Opus 4.5",
             "gemini": "Gemini 3 Flash",
+            "gemini-pro": "Gemini 3 Pro",
         }
         return model_names.get(model, model.title())
 
@@ -270,8 +281,8 @@ class ResearchBot(slixmpp.ClientXMPP):
 
         if model in ("sonnet", "opus"):
             return await self.process_claude(query, model, system_prompt, max_tokens)
-        elif model == "gemini":
-            return await self.process_gemini(query, system_prompt, max_tokens)
+        elif model in ("gemini", "gemini-pro"):
+            return await self.process_gemini(query, model, system_prompt, max_tokens)
         else:
             raise ValueError(f"Unknown model: {model}")
 
@@ -313,32 +324,39 @@ class ResearchBot(slixmpp.ClientXMPP):
 
         return result
 
-    async def process_gemini(self, query: str, system_prompt: str, max_tokens: int) -> str:
+    async def process_gemini(self, query: str, model: str, system_prompt: str, max_tokens: int) -> str:
         """Process query with Gemini"""
         if not self.gemini_client:
             raise RuntimeError("Gemini client not initialized")
 
+        # Map model names to Vertex AI model IDs
+        model_ids = {
+            "gemini": "gemini-3-flash-preview",
+            "gemini-pro": "gemini-3-pro-preview",
+        }
+
         # Combine system prompt with query
         full_prompt = f"{system_prompt}\n\nUser query: {query}"
 
         # Call Gemini API via Vertex AI
         response = self.gemini_client.models.generate_content(
-            model="gemini-3-flash-preview",
+            model=model_ids[model],
             contents=full_prompt,
             config={
                 "max_output_tokens": max_tokens,
                 "temperature": 1.0,
+                "thinking_level": "HIGH",  # Use high thinking level for reasoning
             }
         )
 
         result = response.text
 
-        log.info("Gemini response generated successfully")
+        log.info(f"Gemini ({model_ids[model]}) response generated successfully")
 
         return result
 
-    async def save_to_inbox(self, query: str, result: str, model: str = "Sonnet 4.5"):
-        """Save research result to inbox.org"""
+    async def save_to_file(self, query: str, result: str, filename: str, model: str = "Sonnet 4.5"):
+        """Save research result to org file"""
         timestamp = datetime.now().strftime("%Y-%m-%d %a %H:%M")
 
         entry = f"""* TODO Review research: {query}
@@ -357,12 +375,20 @@ class ResearchBot(slixmpp.ClientXMPP):
 
 """
 
-        # Append to inbox file
-        self.inbox_path.parent.mkdir(parents=True, exist_ok=True)
-        with open(self.inbox_path, "a", encoding="utf-8") as f:
+        # Determine full path
+        if filename.startswith("/"):
+            # Absolute path
+            file_path = Path(filename)
+        else:
+            # Relative to inbox directory
+            file_path = self.inbox_path.parent / filename
+
+        # Append to file
+        file_path.parent.mkdir(parents=True, exist_ok=True)
+        with open(file_path, "a", encoding="utf-8") as f:
             f.write(entry)
 
-        log.info(f"Saved to {self.inbox_path}")
+        log.info(f"Saved to {file_path}")
 
 
 async def main():
modules/xmpp-research-bot/commands.example.yaml
@@ -22,7 +22,7 @@ commands:
       - Aim for 200-500 words unless more detail is requested
     default_model: sonnet
     max_tokens: 2000
-    save_to_inbox: true
+    save_to: inbox.org  # or false/null to not save, or custom filename like "research.org"
 
   summarize:
     description: "Summarize long text or articles"
@@ -39,7 +39,7 @@ commands:
       - Use markdown formatting
     default_model: sonnet
     max_tokens: 1000
-    save_to_inbox: true
+    save_to: inbox.org
 
   analyze:
     description: "Deep analysis of complex topics"
@@ -55,9 +55,9 @@ commands:
       - Highlight trade-offs and implications
       - Use structured markdown with headings
       - Aim for comprehensive analysis (500-1000 words)
-    default_model: opus  # Use more powerful model for analysis
+    default_model: gemini-pro  # Use Gemini 3 Pro for deep reasoning
     max_tokens: 3000
-    save_to_inbox: true
+    save_to: inbox.org
 
   eli5:
     description: "Explain like I'm 5 - simple explanations"
@@ -75,7 +75,7 @@ commands:
       - Use markdown for readability
     default_model: sonnet
     max_tokens: 1500
-    save_to_inbox: false  # Don't save to inbox, just reply
+    save_to: false  # Don't save to file, just reply
 
   translate:
     description: "Translate text between languages"
@@ -92,7 +92,7 @@ commands:
       - Format: "From [language] to [language]:\n\n[translation]"
     default_model: gemini  # Fast and cost-effective for translation
     max_tokens: 2000
-    save_to_inbox: false
+    save_to: false
 
   brainstorm:
     description: "Generate creative ideas and solutions"
@@ -110,4 +110,4 @@ commands:
       - Encourage exploration
     default_model: opus  # More creative with opus
     max_tokens: 2000
-    save_to_inbox: true
+    save_to: brainstorm.org  # Save to separate file
modules/xmpp-research-bot/default.nix
@@ -13,7 +13,7 @@ let
       anthropic
       google-auth
       pyyaml
-      google-generativeai
+      google-genai
     ]
   );
 
systems/aomi/extra.nix
@@ -125,6 +125,7 @@
     vertexProjectId = "itpc-gcp-pnd-pe-eng-claude";
     vertexRegion = "us-east5";
     inboxPath = "/home/vincent/desktop/org/inbox.org";
+    commandsPath = "/home/vincent/.config/xmpp-research-bot/commands.yaml";
     user = "vincent";
     group = "users";
   };