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