diff --git a/.env.example b/.env.example
index bd83feb..dca76cf 100644
--- a/.env.example
+++ b/.env.example
@@ -1,6 +1,9 @@
# Google Gemini
GEMINI_API_KEY=your_gemini_api_key_here
GEMINI_MODEL=gemini-2.5-flash
+# Groq (optional — ultra-low-latency LPU inference)
+# GROQ_API_KEY=your_groq_api_key_here
+# GROQ_MODEL=llama-3.3-70b-versatile
# LLM Settings
TEMPERATURE=0.3
FALLBACK_ORDER='["gemini"]'
diff --git a/plugin/xmem-antigravity/.antigravity-plugin/plugin.json b/plugin/xmem-antigravity/.antigravity-plugin/plugin.json
new file mode 100644
index 0000000..bbcaaee
--- /dev/null
+++ b/plugin/xmem-antigravity/.antigravity-plugin/plugin.json
@@ -0,0 +1,9 @@
+{
+ "name": "xmem-antigravity",
+ "version": "0.1.0",
+ "description": "Persistent XMem memory across Antigravity agent sessions",
+ "author": {
+ "name": "XMem",
+ "email": "support@xmem.in"
+ }
+}
diff --git a/plugin/xmem-antigravity/README.md b/plugin/xmem-antigravity/README.md
new file mode 100644
index 0000000..3175215
--- /dev/null
+++ b/plugin/xmem-antigravity/README.md
@@ -0,0 +1,51 @@
+# XMem Antigravity Plugin
+
+Antigravity plugin for persistent memory through XMem.
+
+This plugin mirrors the structure of `xmem-claude`, adapted for Antigravity's
+agent lifecycle:
+
+- loads relevant project memory on `SessionStart`
+- stores a redacted transcript tail on `Stop`
+- provides `xmem-search` and `xmem-save` skills
+- includes commands for indexing, config, session checks, and logout
+
+## Install
+
+From Antigravity, install this plugin from the local folder once it is published
+or linked by the plugin marketplace flow for this repo.
+
+## Configuration
+
+Use environment variables. Do not commit secrets.
+
+```bash
+export XMEM_API_KEY="xmem_..."
+export XMEM_API_URL="https://api.xmem.in"
+export XMEM_USER_ID="your-user-id"
+```
+
+`XMEM_API_URL` defaults to `https://api.xmem.in`. `XMEM_USER_ID` falls back to the local OS username; production API keys scope requests to the authenticated key owner.
+
+Optional project config can live at `.antigravity/.xmem-antigravity/config.json`:
+
+```json
+{
+ "apiUrl": "https://api.xmem.in",
+ "userId": "your-user-id"
+}
+```
+
+Prefer environment variables for API keys.
+
+## Commands
+
+- `/xmem-antigravity:index` - explore the current repo and save a project summary
+- `/xmem-antigravity:project-config` - show configuration options
+- `/xmem-antigravity:session` - check whether memory is configured
+- `/xmem-antigravity:logout` - remove project-local config
+
+## Skills
+
+- `xmem-search` - search prior XMem memories
+- `xmem-save` - save durable project knowledge
diff --git a/plugin/xmem-antigravity/commands/index.md b/plugin/xmem-antigravity/commands/index.md
new file mode 100644
index 0000000..d6c7496
--- /dev/null
+++ b/plugin/xmem-antigravity/commands/index.md
@@ -0,0 +1,19 @@
+---
+description: Index the current codebase into XMem for future Antigravity context
+allowed-tools: ["Read", "Glob", "Grep", "Bash"]
+---
+
+# Index Codebase Into XMem
+
+Explore the repository and save a concise architecture summary into XMem.
+
+1. Read `README.md`, package manifests, config files, and entry points.
+2. Identify the stack, runtime commands, major modules, API routes, data stores, and conventions.
+3. Skip dependency folders, generated output, lock files, virtual environments, and secrets.
+4. Save the final summary:
+
+```bash
+node "${ANTIGRAVITY_PLUGIN_ROOT}/scripts/save-project-memory.cjs" "SUMMARY_HERE"
+```
+
+Include important files and decisions, but do not save secrets.
diff --git a/plugin/xmem-antigravity/commands/logout.md b/plugin/xmem-antigravity/commands/logout.md
new file mode 100644
index 0000000..403bdc3
--- /dev/null
+++ b/plugin/xmem-antigravity/commands/logout.md
@@ -0,0 +1,16 @@
+---
+description: Remove local XMem Antigravity plugin project config
+allowed-tools: ["Bash"]
+---
+
+# XMem Logout
+
+This plugin does not store credentials by default. It reads `XMEM_API_KEY` from the environment.
+
+To remove project-local config:
+
+```bash
+node -e "const fs=require('fs'); const p='.antigravity/.xmem-antigravity/config.json'; if(fs.existsSync(p)){fs.rmSync(p); console.log('Removed '+p)}else{console.log('No project config found')}"
+```
+
+Also unset `XMEM_API_KEY` in your shell if you want to disconnect this terminal session.
diff --git a/plugin/xmem-antigravity/commands/project-config.md b/plugin/xmem-antigravity/commands/project-config.md
new file mode 100644
index 0000000..aaf3b2a
--- /dev/null
+++ b/plugin/xmem-antigravity/commands/project-config.md
@@ -0,0 +1,25 @@
+---
+description: Show XMem Antigravity plugin configuration options
+allowed-tools: ["Read", "Write", "Bash"]
+---
+
+# XMem Antigravity Configuration
+
+The plugin reads credentials from environment variables first:
+
+```bash
+export XMEM_API_KEY="xmem_..."
+export XMEM_API_URL="https://api.xmem.in"
+export XMEM_USER_ID="your-user-id"
+```
+
+Optional project config lives at `.antigravity/.xmem-antigravity/config.json`:
+
+```json
+{
+ "apiUrl": "https://api.xmem.in",
+ "userId": "your-user-id"
+}
+```
+
+Avoid storing API keys in project config. Prefer environment variables or your shell secret manager.
diff --git a/plugin/xmem-antigravity/commands/session.md b/plugin/xmem-antigravity/commands/session.md
new file mode 100644
index 0000000..f7a3730
--- /dev/null
+++ b/plugin/xmem-antigravity/commands/session.md
@@ -0,0 +1,14 @@
+---
+description: Check whether XMem Antigravity memory is configured
+allowed-tools: ["Bash"]
+---
+
+# XMem Session
+
+Check plugin configuration without printing secrets:
+
+```bash
+node -e "console.log(process.env.XMEM_API_KEY ? 'XMEM_API_KEY is set' : 'XMEM_API_KEY is not set'); console.log('XMEM_API_URL=' + (process.env.XMEM_API_URL || 'https://api.xmem.in'))"
+```
+
+If the key is missing, set `XMEM_API_KEY` before starting Antigravity.
diff --git a/plugin/xmem-antigravity/hooks/hooks.json b/plugin/xmem-antigravity/hooks/hooks.json
new file mode 100644
index 0000000..6df2913
--- /dev/null
+++ b/plugin/xmem-antigravity/hooks/hooks.json
@@ -0,0 +1,27 @@
+{
+ "description": "XMem: Persistent memory for Antigravity",
+ "hooks": {
+ "SessionStart": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "node \"${ANTIGRAVITY_PLUGIN_ROOT}/scripts/context-hook.cjs\"",
+ "timeout": 30
+ }
+ ]
+ }
+ ],
+ "Stop": [
+ {
+ "hooks": [
+ {
+ "type": "command",
+ "command": "node \"${ANTIGRAVITY_PLUGIN_ROOT}/scripts/summary-hook.cjs\"",
+ "timeout": 30
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/plugin/xmem-antigravity/package.json b/plugin/xmem-antigravity/package.json
new file mode 100644
index 0000000..50b24d8
--- /dev/null
+++ b/plugin/xmem-antigravity/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "xmem-antigravity",
+ "version": "0.1.0",
+ "description": "Persistent XMem memory for Antigravity agent sessions",
+ "private": true,
+ "type": "commonjs",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "keywords": [
+ "antigravity",
+ "plugin",
+ "xmem",
+ "memory"
+ ],
+ "license": "MIT"
+}
diff --git a/plugin/xmem-antigravity/scripts/add-memory.cjs b/plugin/xmem-antigravity/scripts/add-memory.cjs
new file mode 100644
index 0000000..c9afd4a
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/add-memory.cjs
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+const { createClient } = require("./lib/xmem-client.cjs");
+const { projectName, redactSecrets, truncate } = require("./lib/plugin-utils.cjs");
+
+async function main() {
+ const content = process.argv.slice(2).join(" ").trim();
+ if (!content) {
+ console.log('Usage: node add-memory.cjs "content to save"');
+ return;
+ }
+
+ try {
+ const cwd = process.cwd();
+ const client = createClient(cwd);
+ await client.ingest(truncate(redactSecrets(content)), {
+ source: "antigravity",
+ type: "manual",
+ project: projectName(cwd),
+ });
+ console.log(`Saved memory to XMem for project: ${projectName(cwd)}`);
+ } catch (error) {
+ console.log(`XMem save failed: ${error.message}`);
+ }
+}
+
+main().catch((error) => {
+ console.error(`Fatal error: ${error.message}`);
+ process.exit(1);
+});
diff --git a/plugin/xmem-antigravity/scripts/context-hook.cjs b/plugin/xmem-antigravity/scripts/context-hook.cjs
new file mode 100644
index 0000000..f9b86cd
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/context-hook.cjs
@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+const { createClient, formatResults } = require("./lib/xmem-client.cjs");
+const { projectName, readStdin, writeJson } = require("./lib/plugin-utils.cjs");
+
+async function main() {
+ const input = await readStdin();
+ const cwd = input.cwd || process.cwd();
+ const project = projectName(cwd);
+
+ try {
+ const client = createClient(cwd);
+ const data = await client.search(`Antigravity project context, architecture, decisions, conventions for ${project}`, 6);
+ const formatted = formatResults(data);
+
+ writeJson({
+ hookSpecificOutput: {
+ hookEventName: "SessionStart",
+ additionalContext: `\n${formatted}\n`,
+ },
+ });
+ } catch (error) {
+ writeJson({
+ hookSpecificOutput: {
+ hookEventName: "SessionStart",
+ additionalContext: `\nXMem memory unavailable: ${error.message}\nSet XMEM_API_KEY to enable Antigravity memory.\n`,
+ },
+ });
+ }
+}
+
+main().catch((error) => {
+ console.error(`XMem fatal: ${error.message}`);
+ process.exit(1);
+});
diff --git a/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs b/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs
new file mode 100644
index 0000000..2ffa1c1
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/lib/plugin-utils.cjs
@@ -0,0 +1,139 @@
+const fs = require("node:fs");
+const os = require("node:os");
+const path = require("node:path");
+
+const MAX_TEXT_LENGTH = 12000;
+
+function readStdin() {
+ return new Promise((resolve) => {
+ let data = "";
+ process.stdin.setEncoding("utf8");
+ process.stdin.on("data", (chunk) => {
+ data += chunk;
+ });
+ process.stdin.on("end", () => {
+ if (!data.trim()) {
+ resolve({});
+ return;
+ }
+ try {
+ resolve(JSON.parse(data));
+ } catch {
+ resolve({});
+ }
+ });
+ });
+}
+
+function writeJson(value) {
+ process.stdout.write(`${JSON.stringify(value)}\n`);
+}
+
+function projectName(cwd = process.cwd()) {
+ return path.basename(path.resolve(cwd));
+}
+
+function projectConfigPath(cwd = process.cwd()) {
+ return path.join(path.resolve(cwd), ".antigravity", ".xmem-antigravity", "config.json");
+}
+
+function globalConfigPath() {
+ return path.join(os.homedir(), ".xmem-antigravity", "settings.json");
+}
+
+function readJsonFile(filePath) {
+ try {
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
+ } catch {
+ return {};
+ }
+}
+
+function loadConfig(cwd = process.cwd()) {
+ return {
+ ...readJsonFile(globalConfigPath()),
+ ...readJsonFile(projectConfigPath(cwd)),
+ };
+}
+
+function truncate(text, limit = MAX_TEXT_LENGTH) {
+ const value = String(text || "").trim();
+ if (value.length <= limit) return value;
+ return `${value.slice(0, limit)}\n\n[truncated]`;
+}
+
+function redactSecrets(text) {
+ return String(text || "")
+ .replace(/xmem_[A-Za-z0-9_-]{12,}/g, "[redacted-xmem-key]")
+ .replace(/sk-[A-Za-z0-9_-]{16,}/g, "[redacted-api-key]")
+ .replace(/gsk_[A-Za-z0-9_-]{16,}/g, "[redacted-groq-key]")
+ .replace(/(authorization\s*[:=]\s*bearer\s+)[^\s"'`]+/gi, "$1[redacted]")
+ .replace(/(bearer\s+)[^\s"'`]+/gi, "$1[redacted]")
+ .replace(/((?:api[_-]?key|authorization|token)\s*[:=]\s*)[^\s"'`]+/gi, "$1[redacted]");
+}
+
+function extractText(value) {
+ if (!value) return "";
+ if (typeof value === "string") return value;
+ if (Array.isArray(value)) return value.map(extractText).filter(Boolean).join("\n");
+ if (typeof value === "object") {
+ if (typeof value.text === "string") return value.text;
+ if (typeof value.content === "string") return value.content;
+ if (value.content) return extractText(value.content);
+ if (value.message) return extractText(value.message);
+ }
+ return "";
+}
+
+/**
+ * Parse an Antigravity transcript.jsonl file into a list of role: text entries.
+ *
+ * Antigravity stores one JSON object per line with the shape:
+ * { step_index, source, type, content, tool_calls? }
+ *
+ * source values: USER_EXPLICIT, MODEL, SYSTEM
+ * type values: USER_INPUT, PLANNER_RESPONSE, VIEW_FILE, RUN_COMMAND, ...
+ */
+function parseAntigravityTranscript(transcriptPath) {
+ if (!transcriptPath || !fs.existsSync(transcriptPath)) return [];
+
+ const lines = fs.readFileSync(transcriptPath, "utf8").split(/\r?\n/).filter(Boolean);
+ const entries = [];
+
+ for (const line of lines) {
+ try {
+ const step = JSON.parse(line);
+ const source = step.source || "entry";
+ const text = extractText(step.content || step.tool_calls || step);
+ if (text.trim()) entries.push(`${source}: ${text.trim()}`);
+ } catch {
+ if (line.trim()) entries.push(line.trim());
+ }
+ }
+
+ return entries;
+}
+
+function transcriptTail(transcriptPath, sessionId, cwd) {
+ const entries = parseAntigravityTranscript(transcriptPath);
+ if (!entries.length) return "";
+
+ const body = entries.slice(-40).join("\n\n");
+ if (!body.trim()) return "";
+
+ return truncate(redactSecrets(`[Antigravity session]\nProject: ${projectName(cwd)}\nSession: ${sessionId || "unknown"}\n\n${body}`));
+}
+
+module.exports = {
+ extractText,
+ globalConfigPath,
+ loadConfig,
+ parseAntigravityTranscript,
+ projectConfigPath,
+ projectName,
+ readStdin,
+ redactSecrets,
+ transcriptTail,
+ truncate,
+ writeJson,
+};
diff --git a/plugin/xmem-antigravity/scripts/lib/xmem-client.cjs b/plugin/xmem-antigravity/scripts/lib/xmem-client.cjs
new file mode 100644
index 0000000..cc75d60
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/lib/xmem-client.cjs
@@ -0,0 +1,99 @@
+const os = require("node:os");
+const { loadConfig } = require("./plugin-utils.cjs");
+
+const DEFAULT_API_URL = "https://api.xmem.in";
+
+class XMemClient {
+ constructor(options = {}) {
+ this.apiUrl = String(options.apiUrl || DEFAULT_API_URL).replace(/\/+$/, "");
+ this.apiKey = options.apiKey;
+ this.userId = options.userId;
+ }
+
+ async request(path, payload) {
+ if (!this.apiKey) {
+ throw new Error("XMEM_API_KEY is not configured.");
+ }
+
+ const response = await fetch(`${this.apiUrl}${path}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${this.apiKey}`,
+ },
+ body: JSON.stringify(payload),
+ });
+
+ const text = await response.text();
+ let body = null;
+ try {
+ body = JSON.parse(text);
+ } catch {
+ body = { error: text };
+ }
+
+ if (!response.ok || body?.status === "error") {
+ throw new Error(body?.error || body?.detail || `XMem request failed with HTTP ${response.status}`);
+ }
+
+ return body?.data ?? body;
+ }
+
+ ingest(content, metadata = {}) {
+ return this.request("/v1/memory/ingest", {
+ user_query: content,
+ agent_response: "",
+ user_id: this.userId,
+ session_datetime: new Date().toISOString(),
+ effort_level: "low",
+ metadata,
+ });
+ }
+
+ search(query, topK = 8) {
+ return this.request("/v1/memory/search", {
+ query,
+ user_id: this.userId,
+ top_k: topK,
+ domains: ["profile", "temporal", "summary"],
+ });
+ }
+}
+
+function createClient(cwd = process.cwd()) {
+ const config = loadConfig(cwd);
+ const userInfo = (() => {
+ try {
+ return os.userInfo().username;
+ } catch {
+ return "antigravity";
+ }
+ })();
+
+ return new XMemClient({
+ apiKey: process.env.XMEM_API_KEY || process.env.XMEM_ANTIGRAVITY_API_KEY || config.apiKey,
+ apiUrl: process.env.XMEM_API_URL || process.env.XMEM_ANTIGRAVITY_API_URL || config.apiUrl || DEFAULT_API_URL,
+ userId: process.env.XMEM_USER_ID || process.env.XMEM_ANTIGRAVITY_USER_ID || config.userId || userInfo || "antigravity",
+ });
+}
+
+function formatResults(data) {
+ const results = data?.results || [];
+ if (!results.length) return "No XMem memories matched.";
+
+ return results
+ .slice(0, 8)
+ .map((item, index) => {
+ const score = typeof item.score === "number" ? ` score=${item.score.toFixed(3)}` : "";
+ const domain = item.domain ? ` domain=${item.domain}` : "";
+ return `${index + 1}.${domain}${score}\n${item.content || ""}`.trim();
+ })
+ .join("\n\n");
+}
+
+module.exports = {
+ DEFAULT_API_URL,
+ XMemClient,
+ createClient,
+ formatResults,
+};
diff --git a/plugin/xmem-antigravity/scripts/save-project-memory.cjs b/plugin/xmem-antigravity/scripts/save-project-memory.cjs
new file mode 100644
index 0000000..cf605a0
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/save-project-memory.cjs
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+const { createClient } = require("./lib/xmem-client.cjs");
+const { projectName, redactSecrets, truncate } = require("./lib/plugin-utils.cjs");
+
+async function main() {
+ const content = process.argv.slice(2).join(" ").trim();
+ if (!content) {
+ console.log('Usage: node save-project-memory.cjs "project knowledge to save"');
+ return;
+ }
+
+ try {
+ const cwd = process.cwd();
+ const client = createClient(cwd);
+ await client.ingest(truncate(redactSecrets(content)), {
+ source: "antigravity",
+ type: "project-knowledge",
+ project: projectName(cwd),
+ });
+ console.log(`Saved project memory to XMem for: ${projectName(cwd)}`);
+ } catch (error) {
+ console.log(`XMem project save failed: ${error.message}`);
+ }
+}
+
+main().catch((error) => {
+ console.error(`Fatal error: ${error.message}`);
+ process.exit(1);
+});
diff --git a/plugin/xmem-antigravity/scripts/search-memory.cjs b/plugin/xmem-antigravity/scripts/search-memory.cjs
new file mode 100644
index 0000000..d017915
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/search-memory.cjs
@@ -0,0 +1,24 @@
+#!/usr/bin/env node
+const { createClient, formatResults } = require("./lib/xmem-client.cjs");
+const { redactSecrets } = require("./lib/plugin-utils.cjs");
+
+async function main() {
+ const query = process.argv.slice(2).join(" ").trim();
+ if (!query) {
+ console.log('Usage: node search-memory.cjs "query"');
+ return;
+ }
+
+ try {
+ const client = createClient(process.cwd());
+ const data = await client.search(redactSecrets(query), 10);
+ console.log(formatResults(data));
+ } catch (error) {
+ console.log(`XMem search failed: ${error.message}`);
+ }
+}
+
+main().catch((error) => {
+ console.error(`Fatal error: ${error.message}`);
+ process.exit(1);
+});
diff --git a/plugin/xmem-antigravity/scripts/summary-hook.cjs b/plugin/xmem-antigravity/scripts/summary-hook.cjs
new file mode 100644
index 0000000..bd3d250
--- /dev/null
+++ b/plugin/xmem-antigravity/scripts/summary-hook.cjs
@@ -0,0 +1,33 @@
+#!/usr/bin/env node
+const { createClient } = require("./lib/xmem-client.cjs");
+const { projectName, readStdin, transcriptTail, writeJson } = require("./lib/plugin-utils.cjs");
+
+async function main() {
+ const input = await readStdin();
+ const cwd = input.cwd || process.cwd();
+ const content = transcriptTail(input.transcript_path, input.session_id, cwd);
+
+ if (!content) {
+ writeJson({ continue: true });
+ return;
+ }
+
+ try {
+ const client = createClient(cwd);
+ await client.ingest(content, {
+ source: "antigravity",
+ type: "session-summary",
+ project: projectName(cwd),
+ session_id: input.session_id || "",
+ });
+ } catch (error) {
+ console.error(`XMem: ${error.message}`);
+ }
+
+ writeJson({ continue: true });
+}
+
+main().catch((error) => {
+ console.error(`XMem fatal: ${error.message}`);
+ process.exit(1);
+});
diff --git a/plugin/xmem-antigravity/skills/xmem-save/SKILL.md b/plugin/xmem-antigravity/skills/xmem-save/SKILL.md
new file mode 100644
index 0000000..142fde3
--- /dev/null
+++ b/plugin/xmem-antigravity/skills/xmem-save/SKILL.md
@@ -0,0 +1,28 @@
+---
+name: xmem-save
+description: Save important project knowledge, decisions, conventions, bug fixes, or implementation context into XMem.
+allowed-tools: Bash(node:*)
+---
+
+# XMem Save
+
+Use this skill when the user asks to remember or save something for future Antigravity sessions.
+
+Format the memory with useful context:
+
+```text
+[SAVE:]
+Project:
+Decision or fact:
+Relevant files:
+Why it matters:
+[/SAVE]
+```
+
+Then run:
+
+```bash
+node "${ANTIGRAVITY_PLUGIN_ROOT}/scripts/save-project-memory.cjs" "FORMATTED_CONTENT"
+```
+
+Never include API keys, tokens, passwords, or other secrets in the saved content.
diff --git a/plugin/xmem-antigravity/skills/xmem-search/SKILL.md b/plugin/xmem-antigravity/skills/xmem-search/SKILL.md
new file mode 100644
index 0000000..1537d67
--- /dev/null
+++ b/plugin/xmem-antigravity/skills/xmem-search/SKILL.md
@@ -0,0 +1,17 @@
+---
+name: xmem-search
+description: Search XMem for prior Antigravity sessions, project decisions, implementation notes, and remembered coding context.
+allowed-tools: Bash(node:*)
+---
+
+# XMem Search
+
+Use this skill when the user asks to recall previous work, earlier implementation details, project decisions, or saved coding memory.
+
+Run:
+
+```bash
+node "${ANTIGRAVITY_PLUGIN_ROOT}/scripts/search-memory.cjs" "USER_QUERY_HERE"
+```
+
+Summarize the returned memories clearly. If nothing useful is returned, ask a sharper follow-up question or try a more specific query.
diff --git a/src/config/settings.py b/src/config/settings.py
index 80857d6..b3afff5 100644
--- a/src/config/settings.py
+++ b/src/config/settings.py
@@ -68,6 +68,18 @@ class Settings(BaseSettings):
default="deepseek-v4-flash",
description="DeepSeek vision model name"
)
+ groq_api_key: Optional[str] = Field(
+ default=None,
+ description="Groq API key"
+ )
+ groq_model: str = Field(
+ default="llama-3.3-70b-versatile",
+ description="Groq model name"
+ )
+ groq_vision_model: str = Field(
+ default="llama-3.2-11b-vision-preview",
+ description="Groq vision-capable model name"
+ )
mimo_api_key: Optional[str] = Field(
default=None,
description="Xiaomi MiMo API key"
@@ -493,7 +505,7 @@ class Settings(BaseSettings):
@field_validator("fallback_order")
@classmethod
def validate_fallback_order(cls, v: List[str]) -> List[str]:
- valid_providers = {"gemini", "claude", "openai", "deepseek", "mimo", "openrouter", "bedrock", "ollama"}
+ valid_providers = {"gemini", "claude", "openai", "deepseek", "groq", "mimo", "openrouter", "bedrock", "ollama"}
for provider in v:
if provider not in valid_providers:
raise ValueError(
diff --git a/src/models/base.py b/src/models/base.py
index f6f65e7..b882392 100644
--- a/src/models/base.py
+++ b/src/models/base.py
@@ -9,6 +9,7 @@
"claude",
"openai",
"deepseek",
+ "groq",
"mimo",
"openrouter",
"bedrock",
diff --git a/src/models/groq.py b/src/models/groq.py
new file mode 100644
index 0000000..b5dd9f8
--- /dev/null
+++ b/src/models/groq.py
@@ -0,0 +1,34 @@
+"""
+Groq model factory.
+
+Uses ChatGroq with Groq's LPU (Language Processing Unit) for ultra-low-latency
+inference — typically 10–20x faster than GPU-based providers. Ideal for XMem's
+memory pipeline where ingest latency is critical.
+"""
+
+from langchain_core.language_models import BaseChatModel
+
+from src.config import settings
+
+
+def build_groq_model(
+ model_name: str | None = None,
+ temperature: float | None = None,
+) -> BaseChatModel:
+ try:
+ from langchain_groq import ChatGroq
+ except ImportError as exc:
+ raise ImportError(
+ "Groq support requires langchain-groq. "
+ "Install it with: pip install -e \".[local]\""
+ ) from exc
+
+ api_key = settings.groq_api_key
+ if not api_key:
+ raise ValueError("GROQ_API_KEY is not set")
+
+ return ChatGroq(
+ api_key=api_key,
+ model=model_name or settings.groq_model,
+ temperature=temperature if temperature is not None else settings.temperature,
+ )
diff --git a/src/models/registry.py b/src/models/registry.py
index 4a2941b..8c3fd16 100644
--- a/src/models/registry.py
+++ b/src/models/registry.py
@@ -34,6 +34,7 @@ def _build_from_module(module_name: str, func_name: str, **kwargs) -> BaseChatMo
"claude": lambda **kw: _build_from_module("claude", "build_claude_model", **kw),
"openai": lambda **kw: _build_from_module("openai", "build_openai_model", **kw),
"deepseek": lambda **kw: _build_from_module("deepseek", "build_deepseek_model", **kw),
+ "groq": lambda **kw: _build_from_module("groq", "build_groq_model", **kw),
"mimo": lambda **kw: _build_from_module("mimo", "build_mimo_model", **kw),
"openrouter": lambda **kw: _build_from_module("openrouter", "build_openrouter_model", **kw),
"bedrock": lambda **kw: _build_from_module("bedrock", "build_bedrock_model", **kw),
@@ -46,6 +47,7 @@ def _build_from_module(module_name: str, func_name: str, **kwargs) -> BaseChatMo
"claude": lambda: settings.claude_api_key,
"openai": lambda: settings.openai_api_key,
"deepseek": lambda: settings.deepseek_api_key,
+ "groq": lambda: settings.groq_api_key,
"mimo": lambda: settings.mimo_api_key,
"openrouter": lambda: settings.openrouter_api_key,
"bedrock": lambda: settings.aws_access_key_id,
@@ -102,6 +104,7 @@ def get_model(
"claude": lambda: settings.claude_vision_model,
"openai": lambda: settings.openai_vision_model,
"deepseek": lambda: settings.deepseek_vision_model,
+ "groq": lambda: settings.groq_vision_model,
"mimo": lambda: settings.mimo_vision_model,
"openrouter": lambda: settings.openrouter_vision_model,
"bedrock": lambda: settings.bedrock_vision_model,