Backend MVP: FastAPI + Claude integration
Core components: - FastAPI server with health endpoints - AI client (Anthropic Claude integration) - Context manager (loads company knowledge) - Chat API (non-streaming and streaming) - Requirements and environment setup Ready to run: python backend/main.py
This commit is contained in:
21
backend/.env.example
Normal file
21
backend/.env.example
Normal file
@@ -0,0 +1,21 @@
|
||||
# Grimlock Backend Configuration
|
||||
|
||||
# API Keys
|
||||
ANTHROPIC_API_KEY=your-anthropic-api-key-here
|
||||
|
||||
# Server Configuration
|
||||
HOST=0.0.0.0
|
||||
PORT=8000
|
||||
DEBUG=true
|
||||
|
||||
# Context Configuration
|
||||
CONTEXT_PATH=/app/context
|
||||
REPOS_PATH=/app/repos
|
||||
|
||||
# AI Configuration
|
||||
AI_MODEL=claude-sonnet-4-5-20250514
|
||||
AI_MAX_TOKENS=4096
|
||||
AI_TEMPERATURE=0.7
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=INFO
|
||||
137
backend/api/chat.py
Normal file
137
backend/api/chat.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Chat API - Main endpoint for interacting with Grimlock
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.responses import StreamingResponse
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
import logging
|
||||
|
||||
from core.context_manager import ContextManager
|
||||
from core.ai_client import AIClient
|
||||
import main
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
class Message(BaseModel):
|
||||
role: str
|
||||
content: str
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
messages: List[Message]
|
||||
role: Optional[str] = None
|
||||
stream: bool = False
|
||||
|
||||
class ChatResponse(BaseModel):
|
||||
response: str
|
||||
context_used: bool
|
||||
|
||||
@router.post("/", response_model=ChatResponse)
|
||||
async def chat(
|
||||
request: ChatRequest,
|
||||
context_manager: ContextManager = Depends(main.get_context_manager),
|
||||
ai_client: AIClient = Depends(main.get_ai_client)
|
||||
):
|
||||
"""
|
||||
Chat with Grimlock
|
||||
|
||||
Args:
|
||||
request: Chat request with messages and optional role
|
||||
|
||||
Returns:
|
||||
ChatResponse with AI response
|
||||
"""
|
||||
try:
|
||||
# Get the last user message for context
|
||||
user_message = None
|
||||
for msg in reversed(request.messages):
|
||||
if msg.role == "user":
|
||||
user_message = msg.content
|
||||
break
|
||||
|
||||
# Get relevant context
|
||||
context = ""
|
||||
if user_message:
|
||||
context = context_manager.get_context_for_query(
|
||||
user_message,
|
||||
role=request.role
|
||||
)
|
||||
|
||||
# Build system prompt
|
||||
system_prompt = context_manager.get_system_prompt(role=request.role)
|
||||
|
||||
# Add context to system prompt if available
|
||||
if context:
|
||||
system_prompt += f"\n\n# Company Context\n{context}"
|
||||
|
||||
# Convert messages to API format
|
||||
api_messages = [
|
||||
{"role": msg.role, "content": msg.content}
|
||||
for msg in request.messages
|
||||
]
|
||||
|
||||
# Get response from AI
|
||||
response = await ai_client.chat(
|
||||
messages=api_messages,
|
||||
system_prompt=system_prompt
|
||||
)
|
||||
|
||||
return ChatResponse(
|
||||
response=response,
|
||||
context_used=bool(context)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in chat endpoint: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/stream")
|
||||
async def chat_stream(
|
||||
request: ChatRequest,
|
||||
context_manager: ContextManager = Depends(main.get_context_manager),
|
||||
ai_client: AIClient = Depends(main.get_ai_client)
|
||||
):
|
||||
"""Stream chat response from Grimlock"""
|
||||
|
||||
try:
|
||||
# Get context
|
||||
user_message = None
|
||||
for msg in reversed(request.messages):
|
||||
if msg.role == "user":
|
||||
user_message = msg.content
|
||||
break
|
||||
|
||||
context = ""
|
||||
if user_message:
|
||||
context = context_manager.get_context_for_query(
|
||||
user_message,
|
||||
role=request.role
|
||||
)
|
||||
|
||||
# Build system prompt
|
||||
system_prompt = context_manager.get_system_prompt(role=request.role)
|
||||
if context:
|
||||
system_prompt += f"\n\n# Company Context\n{context}"
|
||||
|
||||
# Convert messages
|
||||
api_messages = [
|
||||
{"role": msg.role, "content": msg.content}
|
||||
for msg in request.messages
|
||||
]
|
||||
|
||||
# Stream response
|
||||
async def generate():
|
||||
async for chunk in ai_client.chat_stream(
|
||||
messages=api_messages,
|
||||
system_prompt=system_prompt
|
||||
):
|
||||
yield chunk
|
||||
|
||||
return StreamingResponse(generate(), media_type="text/plain")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in chat stream: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
339
backend/context/projects/grimlock.md
Normal file
339
backend/context/projects/grimlock.md
Normal file
@@ -0,0 +1,339 @@
|
||||
# Grimlock
|
||||
|
||||
> **AI-Native Company Operating System**
|
||||
> *Your team's AI companion. One interface. All your context. Infinite intelligence.*
|
||||
|
||||
---
|
||||
|
||||
## What is Grimlock?
|
||||
|
||||
Grimlock is an AI-powered operating system for modern companies. It's like having Jarvis (from Iron Man) for your entire organization - an intelligent assistant that knows everything about your company, can access all your internal systems, and generates any artifact on-demand.
|
||||
|
||||
**The Problem:**
|
||||
- Knowledge workers waste 60-80% of their time finding files, asking for updates, manual data entry, scheduling, and formatting documents
|
||||
- Current tools (Slack, Notion, ChatGPT) are disconnected and lack company context
|
||||
- AI assistants are generic and can't access internal systems
|
||||
- Companies are locked into Microsoft/Google ecosystems
|
||||
|
||||
**The Solution:**
|
||||
One AI interface that:
|
||||
- ✅ Knows everything about your company
|
||||
- ✅ Connects to all your internal systems
|
||||
- ✅ Generates documents, spreadsheets, code, presentations on-demand
|
||||
- ✅ Works for everyone (BD, engineering, ops, finance, assistants)
|
||||
- ✅ Self-hosted for data sovereignty
|
||||
- ✅ Platform-agnostic (not locked to MS/Google)
|
||||
|
||||
---
|
||||
|
||||
## Key Features
|
||||
|
||||
### 🤖 AI-Native Interface
|
||||
- Natural language interaction with company-aware AI
|
||||
- Context preserved across conversations
|
||||
- Role-based responses (BD gets BD-focused, engineers get technical)
|
||||
|
||||
### 🔌 Universal Connectors
|
||||
- Git repositories (GitHub, GitLab, Gitea)
|
||||
- Databases (PostgreSQL, MySQL, MongoDB)
|
||||
- File storage (MinIO, S3, local filesystems)
|
||||
- Calendar systems
|
||||
- Project management tools
|
||||
- Custom internal tools via plugin architecture
|
||||
|
||||
### 📄 Artifact Generation
|
||||
- Documents (Markdown, PDF)
|
||||
- Spreadsheets (CSV, JSON - no Excel dependency)
|
||||
- Code (any language)
|
||||
- Presentations
|
||||
- Reports and dashboards
|
||||
|
||||
### 🏢 Multi-Tenant Architecture
|
||||
- Self-hosted on customer infrastructure
|
||||
- Complete data sovereignty
|
||||
- Role-based access control
|
||||
- SSO integration
|
||||
|
||||
### 🔒 Security First
|
||||
- All data stays on customer infrastructure
|
||||
- Encrypted at rest and in transit
|
||||
- Audit logs for compliance
|
||||
- SOC2, HIPAA-ready architecture
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### For Engineers
|
||||
```
|
||||
Engineer: @grimlock I need to build a payment processing system for construction invoices
|
||||
Grimlock: Based on Vector Zulu patterns, this maps to the UTILEN + Finance Infrastructure patterns:
|
||||
|
||||
Core Stack: FastAPI + PostgreSQL + Redis + Celery
|
||||
Key Patterns: Multi-tenant isolation, background job processing, audit trails
|
||||
Cost Estimate: ~$3-5/transaction for AI processing
|
||||
Timeline: 30 days to working prototype
|
||||
|
||||
Should I generate the project scaffold?
|
||||
```
|
||||
|
||||
### For Business Development
|
||||
```
|
||||
BD: @grimlock create a capabilities deck for a prospective cyber range client
|
||||
Grimlock: [Generates PDF]
|
||||
Created: VectorZulu-CyberRange-Capabilities-2026-02.pdf
|
||||
Download: [link]
|
||||
```
|
||||
|
||||
### For Executives
|
||||
```
|
||||
Exec: @grimlock summarize all active projects and their status
|
||||
Grimlock: Active Projects Summary:
|
||||
|
||||
UTILEN (Document Management) - Production, 499 docs processed
|
||||
Vector Zulu Platform (Cyber Range) - K3s deployment in progress
|
||||
Blockchain Platform - Architecture phase
|
||||
|
||||
Full report: [link to generated document]
|
||||
```
|
||||
|
||||
### For Operations
|
||||
```
|
||||
Ops: @grimlock generate January infrastructure cost report
|
||||
Grimlock: [Generates spreadsheet with cost breakdown]
|
||||
Total: $2,847
|
||||
Breakdown: AI APIs ($1,200), Hosting ($897), Network ($750)
|
||||
Recommendations: Consider tier upgrade for AI API (ROI positive)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Grimlock Web Interface │
|
||||
│ (React/Next.js - ai.yourcompany.com) │
|
||||
└─────────────────┬───────────────────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────────────────┐
|
||||
│ Grimlock Core │
|
||||
│ (FastAPI Backend) │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌──────────────┐ │
|
||||
│ │ AI Layer │ │ Context │ │
|
||||
│ │ (Claude) │ │ Manager │ │
|
||||
│ └─────────────┘ └──────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌──────────────┐ │
|
||||
│ │ Artifact │ │ Connector │ │
|
||||
│ │ Generator │ │ Engine │ │
|
||||
│ └─────────────┘ └──────────────┘ │
|
||||
└─────────────────┬───────────────────────────────┘
|
||||
│
|
||||
┌─────────────────▼───────────────────────────────┐
|
||||
│ Company Data Sources │
|
||||
│ (via Connectors) │
|
||||
│ │
|
||||
│ • Git Repos • Databases • File Storage │
|
||||
│ • Calendars • Project Mgmt • Custom APIs │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
**Backend:**
|
||||
- FastAPI (Python) - API server
|
||||
- PostgreSQL - Metadata and user data
|
||||
- Redis - Caching and task queue
|
||||
- Celery - Background job processing
|
||||
- Anthropic Claude API - AI reasoning
|
||||
|
||||
**Frontend:**
|
||||
- React/Next.js - Web interface
|
||||
- TailwindCSS - Styling
|
||||
- WebSocket - Real-time updates
|
||||
|
||||
**Deployment:**
|
||||
- Docker/Docker Compose - Containerization
|
||||
- Kubernetes - Production orchestration (optional)
|
||||
- Self-hosted on customer infrastructure
|
||||
|
||||
**Connectors:**
|
||||
- Plugin architecture for extensibility
|
||||
- Pre-built connectors for common systems
|
||||
- Custom connector SDK
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Quick Start (Docker Compose)
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://gittea.979labs.com/amitis55/grimlock.git
|
||||
cd grimlock
|
||||
|
||||
# Set up environment variables
|
||||
cp .env.example .env
|
||||
# Edit .env with your configuration
|
||||
|
||||
# Start Grimlock
|
||||
docker-compose up -d
|
||||
|
||||
# Access at http://localhost:3000
|
||||
```
|
||||
|
||||
### Manual Installation
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
python main.py
|
||||
|
||||
# Frontend
|
||||
cd frontend
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
See [Installation Guide](docs/installation.md) for detailed instructions.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
grimlock/
|
||||
├── backend/ # FastAPI backend
|
||||
│ ├── api/ # API endpoints
|
||||
│ ├── core/ # Core business logic
|
||||
│ ├── connectors/ # Data source connectors
|
||||
│ ├── ai/ # AI integration layer
|
||||
│ └── artifacts/ # Artifact generation
|
||||
├── frontend/ # React frontend
|
||||
│ ├── components/ # UI components
|
||||
│ ├── pages/ # Next.js pages
|
||||
│ └── lib/ # Utilities
|
||||
├── connectors/ # Connector plugins
|
||||
│ ├── git/ # Git connector
|
||||
│ ├── database/ # Database connector
|
||||
│ └── ...
|
||||
├── docs/ # Documentation
|
||||
├── docker/ # Docker configurations
|
||||
└── scripts/ # Deployment scripts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
### Phase 1: MVP (Current - Month 3)
|
||||
- [x] Repository setup
|
||||
- [ ] Core backend (FastAPI + Claude integration)
|
||||
- [ ] Basic web interface
|
||||
- [ ] Git connector (read-only)
|
||||
- [ ] Document generation (Markdown, PDF)
|
||||
- [ ] Spreadsheet generation (CSV, JSON)
|
||||
- [ ] Deploy internally at Vector Zulu
|
||||
|
||||
### Phase 2: Beta (Month 4-6)
|
||||
- [ ] Mobile apps (iOS, Android)
|
||||
- [ ] Desktop app (Electron)
|
||||
- [ ] Connector architecture framework
|
||||
- [ ] 10+ pre-built connectors
|
||||
- [ ] Admin dashboard
|
||||
- [ ] Multi-tenant support
|
||||
- [ ] 5-10 beta customers
|
||||
|
||||
### Phase 3: Production (Month 7-12)
|
||||
- [ ] Advanced artifact generation
|
||||
- [ ] Workflow automation
|
||||
- [ ] SSO integrations
|
||||
- [ ] Compliance features (SOC2, HIPAA)
|
||||
- [ ] Self-service onboarding
|
||||
- [ ] Public launch
|
||||
|
||||
### Phase 4: Scale (Year 2+)
|
||||
- [ ] White-label options
|
||||
- [ ] Enterprise features
|
||||
- [ ] API marketplace
|
||||
- [ ] AI model flexibility
|
||||
- [ ] Advanced analytics
|
||||
|
||||
---
|
||||
|
||||
## Business Model
|
||||
|
||||
**Self-Hosted License:**
|
||||
- $50-150/user/month
|
||||
- Deploy on customer infrastructure
|
||||
- Include support and updates
|
||||
|
||||
**Managed Hosting:**
|
||||
- $100-200/user/month
|
||||
- We host on Vector Zulu infrastructure
|
||||
- Full management and support
|
||||
|
||||
**Professional Services:**
|
||||
- Custom connector development
|
||||
- Implementation and training
|
||||
- Enterprise support
|
||||
|
||||
---
|
||||
|
||||
## Why Grimlock?
|
||||
|
||||
**The name:** Grimlock is a powerful Transformer - representing transformation of how companies operate. Just as Grimlock is a leader among Transformers, this platform transforms company operations.
|
||||
|
||||
**The mission:** Eliminate bullshit work. Empower knowledge workers. Make AI-native operations accessible to every company.
|
||||
|
||||
**The vision:** Every company runs on AI-native infrastructure where the AI is the interface to everything.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
[License TBD - Likely dual license: OSS for self-hosted, commercial for managed hosting]
|
||||
|
||||
---
|
||||
|
||||
## Contact
|
||||
|
||||
**Project Owner:** JA @ Vector Zulu LLC
|
||||
**Repository:** https://gittea.979labs.com/amitis55/grimlock
|
||||
**Company:** Vector Zulu LLC
|
||||
|
||||
---
|
||||
|
||||
## Case Study: Vector Zulu
|
||||
|
||||
Grimlock was born from Vector Zulu's need to eliminate operational overhead. We built UTILEN (an enterprise document management system) in 30 days using AI-assisted development - a project others quoted at 18 months. We realized the AI workflows we developed could transform how any company operates.
|
||||
|
||||
Vector Zulu serves as Grimlock's pilot customer, validating every feature in a real production environment serving K-12 cybersecurity education and commercial cloud services.
|
||||
|
||||
**Results:**
|
||||
- 10x faster project delivery
|
||||
- 80% reduction in "where's that file?" questions
|
||||
- Zero Microsoft Office dependencies
|
||||
- Engineers spend time engineering, not finding documents
|
||||
- BD team self-serves on technical content
|
||||
|
||||
---
|
||||
|
||||
**Built with ❤️ by Vector Zulu**
|
||||
*Transforming companies through AI-native operations*
|
||||
|
||||
Grimlock - AI-native company operating system. The Jarvis for modern businesses.
|
||||
96
backend/core/ai_client.py
Normal file
96
backend/core/ai_client.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""
|
||||
AI Client - Anthropic Claude Integration
|
||||
"""
|
||||
|
||||
import anthropic
|
||||
import os
|
||||
import logging
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AIClient:
|
||||
"""Client for interacting with Anthropic Claude API"""
|
||||
|
||||
def __init__(self, api_key: str):
|
||||
self.client = anthropic.Anthropic(api_key=api_key)
|
||||
self.model = os.getenv("AI_MODEL", "claude-sonnet-4-5-20250514")
|
||||
self.max_tokens = int(os.getenv("AI_MAX_TOKENS", "4096"))
|
||||
self.temperature = float(os.getenv("AI_TEMPERATURE", "0.7"))
|
||||
logger.info(f"AIClient initialized with model: {self.model}")
|
||||
|
||||
async def chat(
|
||||
self,
|
||||
messages: List[Dict[str, str]],
|
||||
system_prompt: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None
|
||||
) -> str:
|
||||
"""
|
||||
Send chat messages to Claude and get response
|
||||
|
||||
Args:
|
||||
messages: List of message dicts with 'role' and 'content'
|
||||
system_prompt: Optional system prompt
|
||||
max_tokens: Optional max tokens override
|
||||
|
||||
Returns:
|
||||
Response text from Claude
|
||||
"""
|
||||
try:
|
||||
kwargs = {
|
||||
"model": self.model,
|
||||
"max_tokens": max_tokens or self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
if system_prompt:
|
||||
kwargs["system"] = system_prompt
|
||||
|
||||
response = self.client.messages.create(**kwargs)
|
||||
|
||||
# Extract text from response
|
||||
if response.content and len(response.content) > 0:
|
||||
return response.content[0].text
|
||||
|
||||
return "No response generated"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calling Claude API: {e}")
|
||||
raise
|
||||
|
||||
async def chat_stream(
|
||||
self,
|
||||
messages: List[Dict[str, str]],
|
||||
system_prompt: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None
|
||||
):
|
||||
"""
|
||||
Stream chat response from Claude
|
||||
|
||||
Args:
|
||||
messages: List of message dicts
|
||||
system_prompt: Optional system prompt
|
||||
max_tokens: Optional max tokens override
|
||||
|
||||
Yields:
|
||||
Text chunks from Claude
|
||||
"""
|
||||
try:
|
||||
kwargs = {
|
||||
"model": self.model,
|
||||
"max_tokens": max_tokens or self.max_tokens,
|
||||
"temperature": self.temperature,
|
||||
"messages": messages
|
||||
}
|
||||
|
||||
if system_prompt:
|
||||
kwargs["system"] = system_prompt
|
||||
|
||||
with self.client.messages.stream(**kwargs) as stream:
|
||||
for text in stream.text_stream:
|
||||
yield text
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error streaming from Claude API: {e}")
|
||||
raise
|
||||
171
backend/core/context_manager.py
Normal file
171
backend/core/context_manager.py
Normal file
@@ -0,0 +1,171 @@
|
||||
"""
|
||||
Context Manager - Loads and manages company context
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
import yaml
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class ContextManager:
|
||||
"""Manages loading and accessing company context"""
|
||||
|
||||
def __init__(self, context_path: str):
|
||||
self.context_path = Path(context_path)
|
||||
self.context = {
|
||||
"projects": {},
|
||||
"patterns": {},
|
||||
"anti_patterns": {},
|
||||
"cost_models": {},
|
||||
"repos": {}
|
||||
}
|
||||
self._loaded = False
|
||||
|
||||
def load_all_context(self):
|
||||
"""Load all context from disk"""
|
||||
logger.info(f"Loading context from {self.context_path}")
|
||||
|
||||
# Create context directories if they don't exist
|
||||
self.context_path.mkdir(parents=True, exist_ok=True)
|
||||
for subdir in ["projects", "patterns", "anti_patterns", "cost_models", "repos"]:
|
||||
(self.context_path / subdir).mkdir(exist_ok=True)
|
||||
|
||||
# Load each context type
|
||||
self._load_directory("projects")
|
||||
self._load_directory("patterns")
|
||||
self._load_directory("anti_patterns")
|
||||
self._load_directory("cost_models")
|
||||
|
||||
self._loaded = True
|
||||
logger.info(f"Context loaded: {self.get_summary()}")
|
||||
|
||||
def _load_directory(self, directory: str):
|
||||
"""Load all markdown files from a directory"""
|
||||
dir_path = self.context_path / directory
|
||||
if not dir_path.exists():
|
||||
logger.warning(f"Context directory not found: {dir_path}")
|
||||
return
|
||||
|
||||
for file_path in dir_path.glob("*.md"):
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
self.context[directory][file_path.stem] = content
|
||||
logger.debug(f"Loaded {directory}/{file_path.name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading {file_path}: {e}")
|
||||
|
||||
def get_context_for_query(self, query: str, role: Optional[str] = None) -> str:
|
||||
"""
|
||||
Get relevant context for a query
|
||||
|
||||
Args:
|
||||
query: User query
|
||||
role: User role (engineer, bd, admin, exec)
|
||||
|
||||
Returns:
|
||||
Formatted context string
|
||||
"""
|
||||
# Simple implementation: include all context
|
||||
# TODO: Implement smarter context selection based on query relevance
|
||||
|
||||
context_parts = []
|
||||
|
||||
# Add projects
|
||||
if self.context["projects"]:
|
||||
context_parts.append("# Vector Zulu Projects\n")
|
||||
for name, content in self.context["projects"].items():
|
||||
context_parts.append(f"## {name}\n{content}\n")
|
||||
|
||||
# Add patterns (most relevant for engineers)
|
||||
if self.context["patterns"] and (role == "engineer" or role is None):
|
||||
context_parts.append("\n# Reference Architectures & Patterns\n")
|
||||
for name, content in self.context["patterns"].items():
|
||||
context_parts.append(f"## {name}\n{content}\n")
|
||||
|
||||
# Add anti-patterns
|
||||
if self.context["anti_patterns"]:
|
||||
context_parts.append("\n# Anti-Patterns (Things to Avoid)\n")
|
||||
for name, content in self.context["anti_patterns"].items():
|
||||
context_parts.append(f"## {name}\n{content}\n")
|
||||
|
||||
# Add cost models (relevant for estimates)
|
||||
if "cost" in query.lower() or "price" in query.lower():
|
||||
if self.context["cost_models"]:
|
||||
context_parts.append("\n# Cost Models\n")
|
||||
for name, content in self.context["cost_models"].items():
|
||||
context_parts.append(f"## {name}\n{content}\n")
|
||||
|
||||
return "\n".join(context_parts)
|
||||
|
||||
def get_system_prompt(self, role: Optional[str] = None) -> str:
|
||||
"""
|
||||
Generate system prompt based on role
|
||||
|
||||
Args:
|
||||
role: User role
|
||||
|
||||
Returns:
|
||||
System prompt for Claude
|
||||
"""
|
||||
base_prompt = """You are Grimlock, Vector Zulu's AI assistant. You help team members by:
|
||||
- Answering questions about projects, patterns, and internal systems
|
||||
- Generating documents, spreadsheets, code, and other artifacts
|
||||
- Providing technical guidance based on Vector Zulu's proven patterns
|
||||
- Keeping responses concise and actionable
|
||||
|
||||
You have access to Vector Zulu's internal context including:
|
||||
- Project summaries (UTILEN, Vector Zulu platform, blockchain)
|
||||
- Reference architectures and patterns
|
||||
- Anti-patterns to avoid
|
||||
- Cost models and estimates
|
||||
"""
|
||||
|
||||
role_prompts = {
|
||||
"engineer": "\nYou're speaking with an engineer. Provide technical depth, code examples, and architecture details.",
|
||||
"bd": "\nYou're speaking with business development. Focus on capabilities, timelines, costs, and client-facing information.",
|
||||
"admin": "\nYou're speaking with admin/operations. Focus on processes, reports, schedules, and organizational information.",
|
||||
"exec": "\nYou're speaking with an executive. Provide high-level summaries, key metrics, and strategic insights."
|
||||
}
|
||||
|
||||
if role and role in role_prompts:
|
||||
base_prompt += role_prompts[role]
|
||||
|
||||
return base_prompt
|
||||
|
||||
def is_loaded(self) -> bool:
|
||||
"""Check if context is loaded"""
|
||||
return self._loaded
|
||||
|
||||
def get_summary(self) -> Dict:
|
||||
"""Get summary of loaded context"""
|
||||
return {
|
||||
"projects": len(self.context["projects"]),
|
||||
"patterns": len(self.context["patterns"]),
|
||||
"anti_patterns": len(self.context["anti_patterns"]),
|
||||
"cost_models": len(self.context["cost_models"]),
|
||||
"repos": len(self.context["repos"])
|
||||
}
|
||||
|
||||
def add_context(self, category: str, name: str, content: str):
|
||||
"""Add or update context"""
|
||||
if category in self.context:
|
||||
self.context[category][name] = content
|
||||
logger.info(f"Added/updated {category}/{name}")
|
||||
|
||||
def save_context(self, category: str, name: str):
|
||||
"""Save context to disk"""
|
||||
if category not in self.context or name not in self.context[category]:
|
||||
logger.error(f"Context not found: {category}/{name}")
|
||||
return
|
||||
|
||||
file_path = self.context_path / category / f"{name}.md"
|
||||
try:
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write(self.context[category][name])
|
||||
logger.info(f"Saved {category}/{name} to disk")
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving {file_path}: {e}")
|
||||
106
backend/main.py
Normal file
106
backend/main.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
Grimlock - AI-Native Company Operating System
|
||||
Main FastAPI Application
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from contextlib import asynccontextmanager
|
||||
import logging
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
|
||||
from api.chat import router as chat_router
|
||||
from core.context_manager import ContextManager
|
||||
from core.ai_client import AIClient
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=os.getenv("LOG_LEVEL", "INFO"),
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Global state
|
||||
context_manager = None
|
||||
ai_client = None
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Startup and shutdown events"""
|
||||
global context_manager, ai_client
|
||||
|
||||
logger.info("Starting Grimlock backend...")
|
||||
|
||||
# Initialize context manager
|
||||
context_path = os.getenv("CONTEXT_PATH", "./context")
|
||||
context_manager = ContextManager(context_path)
|
||||
context_manager.load_all_context()
|
||||
logger.info(f"Loaded context from {context_path}")
|
||||
|
||||
# Initialize AI client
|
||||
api_key = os.getenv("ANTHROPIC_API_KEY")
|
||||
if not api_key:
|
||||
logger.error("ANTHROPIC_API_KEY not set!")
|
||||
raise ValueError("ANTHROPIC_API_KEY environment variable is required")
|
||||
|
||||
ai_client = AIClient(api_key=api_key)
|
||||
logger.info("AI client initialized")
|
||||
|
||||
yield
|
||||
|
||||
# Cleanup
|
||||
logger.info("Shutting down Grimlock backend...")
|
||||
|
||||
# Create FastAPI app
|
||||
app = FastAPI(
|
||||
title="Grimlock",
|
||||
description="AI-Native Company Operating System",
|
||||
version="0.1.0",
|
||||
lifespan=lifespan
|
||||
)
|
||||
|
||||
# CORS middleware
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # Configure appropriately for production
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Include routers
|
||||
app.include_router(chat_router, prefix="/api/chat", tags=["chat"])
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
"""Health check endpoint"""
|
||||
return {
|
||||
"status": "online",
|
||||
"service": "Grimlock",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
|
||||
@app.get("/api/health")
|
||||
async def health():
|
||||
"""Detailed health check"""
|
||||
return {
|
||||
"status": "healthy",
|
||||
"context_loaded": context_manager is not None and context_manager.is_loaded(),
|
||||
"ai_client_ready": ai_client is not None
|
||||
}
|
||||
|
||||
def get_context_manager() -> ContextManager:
|
||||
"""Dependency to get context manager"""
|
||||
if context_manager is None:
|
||||
raise HTTPException(status_code=500, detail="Context manager not initialized")
|
||||
return context_manager
|
||||
|
||||
def get_ai_client() -> AIClient:
|
||||
"""Dependency to get AI client"""
|
||||
if ai_client is None:
|
||||
raise HTTPException(status_code=500, detail="AI client not initialized")
|
||||
return ai_client
|
||||
13
backend/requirements.txt
Normal file
13
backend/requirements.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
fastapi==0.109.0
|
||||
uvicorn[standard]==0.27.0
|
||||
anthropic==0.18.1
|
||||
python-dotenv==1.0.0
|
||||
pydantic==2.5.3
|
||||
pydantic-settings==2.1.0
|
||||
python-multipart==0.0.6
|
||||
aiofiles==23.2.1
|
||||
GitPython==3.1.41
|
||||
PyYAML==6.0.1
|
||||
markdown==3.5.2
|
||||
weasyprint==60.2
|
||||
pandas==2.2.0
|
||||
Reference in New Issue
Block a user