diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..e31290af --- /dev/null +++ b/.env.example @@ -0,0 +1,96 @@ +# BrainSait AI Platform - Environment Configuration +# Copy this file to .env and fill in your values + +# ============================================ +# REQUIRED: Core Configuration +# ============================================ + +# GitHub Token for Models API access +GITHUB_TOKEN=your_github_personal_access_token + +# Database password (use a strong password!) +DB_PASSWORD=your_secure_database_password + +# ============================================ +# REQUIRED: API Security +# ============================================ + +# Master API key for admin operations +BRAINSAIT_API_KEY=your_master_api_key_here + +# JWT Secret for user authentication +JWT_SECRET=your_jwt_secret_minimum_32_characters + +# ============================================ +# OPTIONAL: Payment Integration (Stripe) +# ============================================ + +# Stripe API Keys (for monetization) +STRIPE_SECRET_KEY=sk_test_your_stripe_secret_key +STRIPE_PUBLISHABLE_KEY=pk_test_your_stripe_publishable_key +STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret + +# Stripe Price IDs for subscription tiers +STRIPE_PRICE_PRO=price_xxxxxxxxxxxxx +STRIPE_PRICE_ENTERPRISE=price_xxxxxxxxxxxxx + +# ============================================ +# OPTIONAL: Monitoring & Analytics +# ============================================ + +# Grafana admin password +GRAFANA_PASSWORD=your_grafana_admin_password + +# Sentry DSN for error tracking +SENTRY_DSN=https://xxxx@sentry.io/xxxxx + +# ============================================ +# OPTIONAL: Email (for notifications) +# ============================================ + +# SMTP Configuration +SMTP_HOST=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_USER=apikey +SMTP_PASSWORD=your_sendgrid_api_key +SMTP_FROM=noreply@brainsait.ai + +# ============================================ +# OPTIONAL: Cloud Storage (for audit logs) +# ============================================ + +# AWS S3 (or compatible) +AWS_ACCESS_KEY_ID=your_aws_access_key +AWS_SECRET_ACCESS_KEY=your_aws_secret_key +AWS_REGION=me-south-1 +S3_BUCKET=brainsait-audit-logs + +# ============================================ +# OPTIONAL: Domain-Specific Settings +# ============================================ + +# Arabic Market +ARABIC_DEFAULT_MODEL=openai/gpt-4o +ARABIC_TRANSLATION_API=your_translation_api_key + +# Healthcare (HIPAA) +HIPAA_AUDIT_ENABLED=true +PHI_ENCRYPTION_KEY=your_32_byte_encryption_key_here + +# ============================================ +# Application Settings +# ============================================ + +# Log level: debug, info, warn, error +LOG_LEVEL=info + +# Server port +PORT=8080 + +# Environment: development, staging, production +ENVIRONMENT=development + +# Rate limiting (requests per minute) +RATE_LIMIT_FREE=60 +RATE_LIMIT_PRO=600 +RATE_LIMIT_ENTERPRISE=6000 diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index f741ab43..87084b5f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -27,18 +27,35 @@ This repository implements the GitHub Models CLI extension (`gh models`), enabli 3. Azure client converts to `azuremodels.ChatCompletionOptions` and makes API calls 4. Results are formatted using terminal-aware table printers from `command.Config` +### Generate Command (PromptPex) +- **Location**: `cmd/generate/` - self-contained implementation of test generation +- **Key files**: `pipeline.go` (orchestration), `prompts.go` (phase prompts), `evaluators.go` (test evaluation) +- **Flow**: Intent → InputSpec → OutputRules → InverseOutputRules → Tests → Evaluation +- **Session state**: `types.go` defines `PromptPexContext` - serialized to JSON for resumability +- **Cleanup**: `cleaner.go` handles post-generation test refinement and deduplication + ## Developer Workflows ### Building & Testing - **Local build**: `make build` or `script/build` (creates `gh-models` binary) - **Cross-platform**: `script/build all|windows|linux|darwin` for release builds + - Builds for windows/amd64, linux/amd64, android/arm64, android/amd64, darwin/amd64, darwin/arm64 - **Testing**: `make check` runs format, vet, tidy, and tests. Use `go test ./...` directly for faster iteration - **Quality gates**: `make check` - required before commits +- **Integration tests**: `make integration` - builds binary and runs tests against live endpoints (requires `gh auth login`) + - Located in `integration/` directory with separate go.mod + - Tests gracefully skip when authentication unavailable + - CI runs these via `.github/workflows/integration.yml` ### Authentication & Setup - Extension requires `gh auth login` before use - unauthenticated clients show helpful error messages - Client initialization pattern in `cmd/root.go`: check token, create appropriate client (authenticated vs unauthenticated) +### Release Process +- Create git tag: `git tag v0.0.x main && git push origin tag v0.0.x` +- This triggers `.github/workflows/release.yml` for production builds +- Users install with `gh extension install github/gh-models` (pulls latest release, not latest commit) + ## Prompt File Conventions ### Structure (.prompt.yml) @@ -62,13 +79,21 @@ evaluators: - **JSON Schema**: Use `responseFormat: json_schema` with `jsonSchema` field containing strict JSON schema - **Templates**: All message content supports `{{variable}}` substitution from `testData` entries +### PromptPex Test Generation +- **Command**: `gh models generate` - implements [PromptPex](https://github.com/microsoft/promptpex) methodology +- **Effort levels**: `min` (quick validation), `low` (limits to 3 rules), `medium` (better coverage), `high` (complex inputs, more tokens) +- **Custom instructions**: Use `--instruction-{phase}` flags for intent, inputspec, outputrules, inverseoutputrules, tests +- **Session persistence**: `--session-file` to save/load generation state +- **Documentation**: See `cmd/generate/README.md` and https://microsoft.github.io/promptpex/reference/test-generation/ + ## Testing Patterns -### Command Tests +### Unit Tests (Command Tests) - **Location**: `cmd/{command}/{command}_test.go` - **Pattern**: Create mock client via `azuremodels.NewMockClient()`, inject into `command.Config` - **Structure**: Table-driven tests with subtests using `t.Run()` - **Assertions**: Use `testify/require` for cleaner error messages +- **Run**: `make test` or `go test -race -cover ./...` ### Mock Usage ```go @@ -76,6 +101,14 @@ client := azuremodels.NewMockClient() cfg := command.NewConfig(new(bytes.Buffer), new(bytes.Buffer), client, true, 80) ``` +### Integration Tests +- **Location**: `integration/integration_test.go` (separate go.mod) +- **Pattern**: Execute compiled binary via `exec.Command()` with timeout protection +- **Prerequisites**: Binary must exist (`make build` first) +- **Authentication**: Tests skip gracefully when `gh auth` unavailable +- **Focus**: Verify basic functionality, command execution, output format - not full feature testing +- **Run**: `make integration` (combines `make check`, `make build`, and integration tests) + ## Integration Points ### GitHub Authentication diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 00000000..d6c39af2 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,63 @@ +name: Build and Push Docker Image + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + branches: [main] + workflow_dispatch: + +env: + REGISTRY: docker.io + IMAGE_NAME: brainsait/api + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix= + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile.api + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: linux/amd64,linux/arm64 + + - name: Image digest + if: github.event_name != 'pull_request' + run: echo "Image pushed with digest ${{ steps.docker_build.outputs.digest }}" diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..cb99a895 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-docker" + ] +} \ No newline at end of file diff --git a/BRAINSAIT_CUSTOMIZATION.md b/BRAINSAIT_CUSTOMIZATION.md new file mode 100644 index 00000000..b863a2fc --- /dev/null +++ b/BRAINSAIT_CUSTOMIZATION.md @@ -0,0 +1,452 @@ +# BrainSait AI Platform - Customization Guide + +> Transform `gh-models` into a branded, monetizable AI platform for targeted domains. + +## ✅ Legal Status + +**MIT License allows:** +- ✅ Commercial use +- ✅ Modification & rebranding +- ✅ Distribution +- ✅ Monetization +- ⚠️ Must include original license & copyright + +--- + +## 🐳 1. Containerization + +### Dockerfile + +```dockerfile +# Build stage +FROM golang:1.23-alpine AS builder +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o brainsait-models main.go + +# Runtime stage +FROM alpine:3.19 +RUN apk --no-cache add ca-certificates +WORKDIR /root/ +COPY --from=builder /app/brainsait-models . +EXPOSE 8080 +ENTRYPOINT ["./brainsait-models"] +``` + +### Docker Compose (with API wrapper) + +```yaml +version: '3.8' +services: + brainsait-api: + build: . + ports: + - "8080:8080" + environment: + - GITHUB_TOKEN=${GITHUB_TOKEN} + - BRAINSAIT_API_KEY=${BRAINSAIT_API_KEY} + restart: unless-stopped + + redis: + image: redis:alpine + ports: + - "6379:6379" + volumes: + - redis-data:/data + + postgres: + image: postgres:15-alpine + environment: + POSTGRES_DB: brainsait + POSTGRES_USER: admin + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres-data:/var/lib/postgresql/data + +volumes: + redis-data: + postgres-data: +``` + +--- + +## 🏷️ 2. Branding Customization + +### Files to Modify: + +| File | Changes | +|------|---------| +| `cmd/root.go` | Update command names, descriptions | +| `internal/azuremodels/legal.go` | Update NOTICE | +| `go.mod` | Change module name | +| `README.md` | Complete rebrand | + +### Example: cmd/root.go changes + +```go +// Before +cmd := &cobra.Command{ + Use: "models", + Short: "GitHub Models extension", + Long: heredoc.Docf(` + GitHub Models CLI extension... + `, "`"), +} + +// After +cmd := &cobra.Command{ + Use: "brainsait", + Short: "BrainSait AI Platform", + Long: heredoc.Docf(` + BrainSait AI Platform - Enterprise AI for Healthcare, Arabic Markets & Developers. + + Powered by advanced language models with domain-specific optimizations. + `, "`"), +} +``` + +--- + +## 🎯 3. Domain-Specific Customizations + +### 3.1 Arabic Speakers Market 🌍 + +**Create: `prompts/arabic/`** + +```yaml +# prompts/arabic/medical_ar.prompt.yml +name: "Arabic Medical Assistant" +model: "openai/gpt-4o" +messages: + - role: system + content: | + أنت مساعد طبي متخصص باللغة العربية. + قدم معلومات طبية دقيقة وواضحة. + استخدم المصطلحات الطبية العربية الفصحى. + + - role: user + content: "{{query}}" +testData: + - query: "ما هي أعراض مرض السكري؟" + - query: "كيف أتعامل مع ارتفاع ضغط الدم؟" +``` + +**Add RTL support in output:** + +```go +// pkg/util/rtl.go +package util + +import ( + "strings" + "unicode" +) + +func IsArabic(s string) bool { + for _, r := range s { + if unicode.Is(unicode.Arabic, r) { + return true + } + } + return false +} + +func FormatRTL(s string) string { + if IsArabic(s) { + return "\u200F" + s + "\u200F" // RTL marks + } + return s +} +``` + +### 3.2 Healthcare & Insurance 🏥 + +**Create: `prompts/healthcare/`** + +```yaml +# prompts/healthcare/insurance_claim.prompt.yml +name: "Insurance Claim Analyzer" +model: "openai/gpt-4o" +messages: + - role: system + content: | + You are a healthcare insurance claims analyst. + Analyze claims for: + - Medical necessity + - Coding accuracy (ICD-10, CPT) + - Policy coverage alignment + - Potential fraud indicators + + IMPORTANT: Never provide definitive coverage decisions. + Always recommend human review for final determination. + + - role: user + content: | + Analyze this claim: + {{claim_details}} + + Policy: {{policy_type}} +testData: + - claim_details: "Emergency room visit, diagnosis: chest pain" + policy_type: "PPO Standard" +``` + +**HIPAA Compliance additions:** + +```go +// internal/compliance/hipaa.go +package compliance + +import ( + "crypto/aes" + "log" +) + +type AuditLog struct { + Timestamp time.Time + UserID string + Action string + DataAccessed string + IPAddress string +} + +func LogPHIAccess(log AuditLog) error { + // Implement HIPAA-compliant audit logging +} + +func SanitizePHI(input string) string { + // Remove/mask potential PHI before sending to external APIs +} +``` + +### 3.3 Developer Tools 💻 + +**Create: `prompts/developer/`** + +```yaml +# prompts/developer/code_review.prompt.yml +name: "Code Review Assistant" +model: "openai/gpt-4o" +messages: + - role: system + content: | + You are an expert code reviewer. Analyze code for: + - Security vulnerabilities + - Performance issues + - Best practices adherence + - Potential bugs + + Provide specific, actionable feedback with examples. + + - role: user + content: | + Review this {{language}} code: + ```{{language}} + {{code}} + ``` +testData: + - language: "go" + code: "func main() { fmt.Println(\"hello\") }" +``` + +--- + +## 💰 4. Monetization Architecture + +### API Gateway Layer + +```go +// cmd/api/main.go +package main + +import ( + "github.com/gin-gonic/gin" + "github.com/brainsait/platform/internal/billing" + "github.com/brainsait/platform/internal/auth" +) + +func main() { + r := gin.Default() + + // Middleware + r.Use(auth.APIKeyMiddleware()) + r.Use(billing.UsageTracker()) + r.Use(billing.RateLimiter()) + + // Routes + r.POST("/v1/chat", handlers.Chat) + r.POST("/v1/eval", handlers.Eval) + r.POST("/v1/generate", handlers.Generate) + + // Admin + r.GET("/admin/usage", handlers.GetUsage) + r.GET("/admin/billing", handlers.GetBilling) + + r.Run(":8080") +} +``` + +### Billing Service + +```go +// internal/billing/tracker.go +package billing + +type UsageTier struct { + Name string + MonthlyCredits int + PricePerCredit float64 + Features []string +} + +var Tiers = map[string]UsageTier{ + "free": { + Name: "Free", + MonthlyCredits: 100, + PricePerCredit: 0, + Features: []string{"basic_models", "community_support"}, + }, + "pro": { + Name: "Professional", + MonthlyCredits: 10000, + PricePerCredit: 0.001, + Features: []string{"all_models", "priority_support", "arabic_prompts"}, + }, + "enterprise": { + Name: "Enterprise", + MonthlyCredits: -1, // unlimited + PricePerCredit: 0, + Features: []string{"all_models", "dedicated_support", "custom_models", "hipaa", "on_premise"}, + }, +} + +func TrackUsage(userID string, credits int) error { + // Track API usage against user's plan +} +``` + +### Stripe Integration + +```go +// internal/billing/stripe.go +package billing + +import ( + "github.com/stripe/stripe-go/v76" + "github.com/stripe/stripe-go/v76/subscription" +) + +func CreateSubscription(customerID, priceID string) (*stripe.Subscription, error) { + params := &stripe.SubscriptionParams{ + Customer: stripe.String(customerID), + Items: []*stripe.SubscriptionItemsParams{ + {Price: stripe.String(priceID)}, + }, + } + return subscription.New(params) +} +``` + +--- + +## 📊 5. Database Schema + +```sql +-- Users +CREATE TABLE users ( + id UUID PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + api_key VARCHAR(64) UNIQUE NOT NULL, + tier VARCHAR(50) DEFAULT 'free', + created_at TIMESTAMP DEFAULT NOW() +); + +-- Usage tracking +CREATE TABLE usage_logs ( + id SERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id), + endpoint VARCHAR(100), + credits_used INT, + model_used VARCHAR(100), + domain VARCHAR(50), -- 'arabic', 'healthcare', 'developer' + timestamp TIMESTAMP DEFAULT NOW() +); + +-- Billing +CREATE TABLE billing_records ( + id SERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id), + stripe_subscription_id VARCHAR(100), + amount_cents INT, + status VARCHAR(50), + period_start DATE, + period_end DATE +); +``` + +--- + +## 🚀 6. Quick Start + +### Step 1: Fork & Clone +```bash +gh repo fork github/gh-models --clone=true +cd gh-models +git remote rename origin upstream +``` + +### Step 2: Rebrand +```bash +# Update module name +sed -i '' 's/github.com\/github\/gh-models/github.com\/brainsait\/platform/g' go.mod +find . -name "*.go" -exec sed -i '' 's/github.com\/github\/gh-models/github.com\/brainsait\/platform/g' {} \; +``` + +### Step 3: Build Container +```bash +docker build -t brainsait-platform:latest . +docker run -p 8080:8080 -e GITHUB_TOKEN=$GITHUB_TOKEN brainsait-platform:latest +``` + +### Step 4: Deploy +```bash +# Google Cloud Run +gcloud run deploy brainsait-api --source . --region=me-central1 + +# Or Kubernetes +kubectl apply -f k8s/deployment.yaml +``` + +--- + +## 📈 Revenue Projections + +| Vertical | Target Market Size | Year 1 Goal | +|----------|-------------------|-------------| +| Arabic Speakers | 400M+ speakers | 1,000 users | +| Healthcare/Insurance | $4T industry | 50 enterprise clients | +| Developers | 27M+ worldwide | 5,000 users | + +**Estimated ARR (Year 1):** $150K - $500K + +--- + +## ⚠️ Important Considerations + +1. **Model Access**: GitHub Models is in preview; consider alternative model providers for production +2. **Compliance**: Healthcare requires HIPAA BAA with model providers +3. **Data Residency**: Arabic market may require local data hosting +4. **Support**: Plan for Arabic-speaking support staff + +--- + +## Next Steps + +1. [ ] Fork repository +2. [ ] Create BrainSait organization on GitHub +3. [ ] Set up cloud infrastructure +4. [ ] Build MVP for one vertical first (recommend: Developers) +5. [ ] Validate with beta customers +6. [ ] Iterate and expand to other verticals diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..7e12440d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,59 @@ +# BrainSait AI Platform - Production Dockerfile +# Multi-stage build for minimal image size + +# ============================================ +# Stage 1: Build +# ============================================ +FROM golang:1.23-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git ca-certificates tzdata + +WORKDIR /app + +# Cache dependencies +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build binary with optimizations +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags="-w -s -X main.version=$(git describe --tags --always)" \ + -o brainsait-models \ + main.go + +# ============================================ +# Stage 2: Runtime +# ============================================ +FROM alpine:3.19 + +# Security: run as non-root user +RUN addgroup -g 1000 brainsait && \ + adduser -u 1000 -G brainsait -s /bin/sh -D brainsait + +# Install runtime dependencies +RUN apk --no-cache add ca-certificates tzdata + +WORKDIR /app + +# Copy binary from builder +COPY --from=builder /app/brainsait-models . + +# Copy domain-specific prompts (if any) +COPY --from=builder /app/examples ./prompts + +# Set ownership +RUN chown -R brainsait:brainsait /app + +# Switch to non-root user +USER brainsait + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD ./brainsait-models list || exit 1 + +# Default command +ENTRYPOINT ["./brainsait-models"] +CMD ["--help"] diff --git a/Dockerfile.api b/Dockerfile.api new file mode 100644 index 00000000..a9dfa686 --- /dev/null +++ b/Dockerfile.api @@ -0,0 +1,72 @@ +# BrainSait API Server Dockerfile +# Multi-stage build for optimized production image + +# ================================ +# Stage 1: Build +# ================================ +FROM golang:1.22-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git ca-certificates tzdata + +WORKDIR /app + +# Copy go mod files +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build CLI binary +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -ldflags="-w -s" -o /app/bin/gh-models . + +# Build API server binary +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -ldflags="-w -s" -o /app/bin/brainsait-api ./cmd/api + +# ================================ +# Stage 2: Production +# ================================ +FROM alpine:3.19 + +# Install runtime dependencies +RUN apk add --no-cache ca-certificates tzdata curl wget + +# Create non-root user +RUN addgroup -g 1000 brainsait && \ + adduser -u 1000 -G brainsait -s /bin/sh -D brainsait + +WORKDIR /app + +# Copy binaries from builder +COPY --from=builder /app/bin/gh-models /usr/local/bin/ +COPY --from=builder /app/bin/brainsait-api /usr/local/bin/ + +# Copy example prompts +COPY --from=builder /app/examples /app/examples + +# Copy i18n locales +COPY --from=builder /app/internal/i18n/locales /app/locales + +# Set ownership +RUN chown -R brainsait:brainsait /app + +# Switch to non-root user +USER brainsait + +# Environment variables +ENV PORT=8080 +ENV LOG_LEVEL=info +ENV ENVIRONMENT=production + +# Expose API port +EXPOSE 8080 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget -q --spider http://localhost:8080/health || exit 1 + +# Default command - run API server +CMD ["brainsait-api"] diff --git a/README.md b/README.md index 9e06e0c9..a2fab279 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,17 @@ Use the GitHub Models service from the CLI! This repository implements the GitHub Models CLI extension (`gh models`), enabling users to interact with AI models via the `gh` CLI. The extension supports inference, prompt evaluation, model listing, and test generation. +### BrainSait AI Platform + +This repository also includes **BrainSait** - a complete AI platform for building domain-specific AI agents: + +- 🌍 **Arabic Assistant** - Multi-agent system for Arabic speakers (translation, writing, research) +- 🏥 **Healthcare Insurance** - HIPAA-compliant claims analysis, prior auth, coding, appeals +- 💻 **Developer Assistant** - Code review, architecture, debugging, testing, DevOps +- 🔍 **SQL Analyst** - Natural language to SQL with data analysis + +See [compose-agents/](./compose-agents/) for Docker Compose-based multi-agent systems powered by [Docker Compose for Agents](https://github.com/docker/compose-for-agents). + ## Using ### Prerequisites diff --git a/agents/README.md b/agents/README.md new file mode 100644 index 00000000..f6e3a6d1 --- /dev/null +++ b/agents/README.md @@ -0,0 +1,164 @@ +# BrainSait AI Agents + +Pre-built AI agents powered by [Docker cagent](https://github.com/docker/cagent) for specialized domains. + +## Quick Start + +### Install cagent + +```bash +# macOS/Linux +brew install docker/tap/cagent + +# Or download from releases +curl -L https://github.com/docker/cagent/releases/latest/download/cagent-$(uname -s)-$(uname -m) -o cagent +chmod +x cagent +sudo mv cagent /usr/local/bin/ +``` + +### Set up GitHub Token + +```bash +export GITHUB_TOKEN=$(gh auth token) +# Or use your personal access token +export GITHUB_TOKEN=ghp_xxxxx +``` + +### Run an Agent + +```bash +# Arabic Language Assistant +cagent run agents/brainsait-arabic.yaml + +# Healthcare Insurance Analyst +cagent run agents/brainsait-healthcare.yaml + +# Developer Assistant +cagent run agents/brainsait-developer.yaml +``` + +## Available Agents + +### 🌍 Arabic Language Assistant (`brainsait-arabic.yaml`) + +Specialized AI assistant for Arabic speakers: +- **Fluent Arabic** - Modern Standard Arabic and dialects +- **Translation** - Arabic ↔ English translation +- **Cultural awareness** - Respects Arab/Islamic culture +- **Technical help** - Explains concepts in Arabic + +**Commands:** +- `/translate` - Translate text to Arabic +- `/explain` - Explain in Arabic +- `/summarize` - Summarize content in Arabic + +### 🏥 Healthcare Insurance Analyst (`brainsait-healthcare.yaml`) + +HIPAA-aware assistant for healthcare insurance: +- **Claims Analysis** - Review claims for completeness +- **Prior Authorization** - PA requirements and documentation +- **Medical Coding** - ICD-10, CPT, HCPCS verification +- **Appeals Support** - Help with denial appeals + +**Commands:** +- `/analyze-claim` - Analyze insurance claim +- `/check-auth` - Check PA requirements +- `/appeal-help` - Draft appeal letter +- `/code-check` - Verify medical codes + +**Multi-agent team:** +- `claims_analyst` - Claims processing specialist +- `prior_auth_specialist` - Prior authorization expert +- `coding_expert` - Medical coding specialist + +### 💻 Developer Assistant (`brainsait-developer.yaml`) + +AI development team for software engineering: +- **Code Review** - Quality, security, performance +- **Architecture** - System design and decisions +- **Debugging** - Troubleshooting and fixes +- **Testing** - Test generation and strategies +- **DevOps** - CI/CD and deployment + +**Commands:** +- `/review` - Review code +- `/explain` - Explain code +- `/refactor` - Suggest improvements +- `/test` - Generate tests +- `/doc` - Generate documentation + +**Multi-agent team:** +- `code_reviewer` - Senior code reviewer +- `architect` - Software architect +- `debugger` - Debugging specialist +- `tester` - QA engineer +- `devops` - DevOps engineer + +## Integration with BrainSait API + +These agents use GitHub Models as the LLM provider, which integrates seamlessly with the BrainSait API: + +```yaml +providers: + github: + type: openai + base_url: https://models.inference.ai.azure.com + api_key: ${GITHUB_TOKEN} +``` + +To use BrainSait API instead: + +```yaml +providers: + brainsait: + type: openai + base_url: https://api.brainsait.ai/v1 + api_key: ${BRAINSAIT_API_KEY} +``` + +## MCP Tools + +Agents use Docker MCP (Model Context Protocol) servers for extended capabilities: + +| Tool | Description | +|------|-------------| +| `docker:duckduckgo` | Web search | +| `docker:github-official` | GitHub integration | +| `docker:filesystem` | File operations | + +Configure MCP servers in Docker Desktop's MCP Toolkit. + +## Creating Custom Agents + +1. Copy an existing agent as template +2. Modify the instruction and tools +3. Test with `cagent run your-agent.yaml` +4. Share via Git or Docker Hub + +Example minimal agent: + +```yaml +agents: + root: + model: github-gpt4o + description: "My Custom Agent" + instruction: | + You are a helpful assistant specialized in... + +models: + github-gpt4o: + provider: github + model: openai/gpt-4o + +providers: + github: + type: openai + base_url: https://models.inference.ai.azure.com + api_key: ${GITHUB_TOKEN} +``` + +## Support + +- **Documentation**: https://docs.brainsait.ai/agents +- **Issues**: https://github.com/github/gh-models/issues +- **Discord**: https://discord.gg/brainsait diff --git a/agents/brainsait-arabic.yaml b/agents/brainsait-arabic.yaml new file mode 100644 index 00000000..edcc42d6 --- /dev/null +++ b/agents/brainsait-arabic.yaml @@ -0,0 +1,73 @@ +#!/usr/bin/env cagent run + +# BrainSait Arabic Language AI Agent +# Specialized assistant for Arabic speakers + +agents: + root: + model: github-gpt4o + description: "مساعد ذكاء اصطناعي متخصص باللغة العربية - BrainSait Arabic Assistant" + instruction: | + أنت مساعد ذكاء اصطناعي متخصص يتحدث العربية الفصحى واللهجات المختلفة. + + قواعد أساسية: + - تحدث دائماً باللغة العربية إلا إذا طلب المستخدم غير ذلك + - استخدم الأرقام العربية (١، ٢، ٣) عند الكتابة بالعربية + - احترم الثقافة العربية والإسلامية في ردودك + - كن مهذباً ومحترماً دائماً + - قدم معلومات دقيقة وموثوقة + + You are a specialized AI assistant fluent in Modern Standard Arabic + and various Arabic dialects. You can help with: + - Translation between Arabic and English + - Arabic language learning + - Cultural questions about the Arab world + - Technical explanations in Arabic + - Document analysis and summarization + + toolsets: + - type: mcp + ref: docker:duckduckgo + instruction: | + Use web search to find relevant Arabic content and resources. + Prefer Arabic language sources when available. + + - type: mcp + ref: docker:github-official + instruction: | + Help users with GitHub repositories and code in Arabic. + + commands: + translate: "ترجم النص التالي إلى العربية" + explain: "اشرح بالعربية" + summarize: "لخص المحتوى التالي بالعربية" + + translator: + model: github-gpt4o + description: "مترجم متخصص - Arabic-English Translator" + instruction: | + أنت مترجم محترف متخصص في الترجمة بين العربية والإنجليزية. + + قواعد الترجمة: + - حافظ على المعنى الأصلي + - استخدم مصطلحات دقيقة + - راعِ السياق الثقافي + - وضح المصطلحات التقنية عند الحاجة + +models: + github-gpt4o: + provider: github + model: openai/gpt-4o + max_tokens: 8192 + + github-gpt4o-mini: + provider: github + model: openai/gpt-4o-mini + max_tokens: 4096 + +# Provider configurations +providers: + github: + type: openai + base_url: https://models.inference.ai.azure.com + api_key: ${GITHUB_TOKEN} diff --git a/agents/brainsait-developer.yaml b/agents/brainsait-developer.yaml new file mode 100644 index 00000000..b6705e4f --- /dev/null +++ b/agents/brainsait-developer.yaml @@ -0,0 +1,143 @@ +#!/usr/bin/env cagent run + +# BrainSait Developer Agent +# Multi-agent development team for software engineering + +agents: + root: + model: github-gpt4o + description: "BrainSait Developer Assistant - Your AI Development Team" + instruction: | + You are the lead of an AI development team that helps developers with: + - Code review and analysis + - Architecture design and decisions + - Debugging and troubleshooting + - Documentation generation + - Testing and quality assurance + - DevOps and deployment + + You can delegate tasks to specialized team members: + - code_reviewer: For in-depth code analysis + - architect: For system design questions + - debugger: For troubleshooting issues + - tester: For testing strategies + - devops: For CI/CD and deployment + + Always provide practical, actionable advice with code examples when relevant. + + toolsets: + - type: mcp + ref: docker:github-official + instruction: | + Access GitHub repositories, issues, PRs, and actions. + Use for code search, repo analysis, and GitHub operations. + + - type: mcp + ref: docker:duckduckgo + instruction: | + Search for documentation, Stack Overflow answers, and best practices. + + - type: mcp + ref: docker:filesystem + instruction: | + Read and analyze local code files. + tools: ["read_file", "write_file", "list_directory"] + + commands: + review: "Review this code for issues and improvements" + explain: "Explain how this code works" + refactor: "Suggest refactoring for this code" + test: "Generate tests for this code" + doc: "Generate documentation for this code" + + code_reviewer: + model: github-gpt4o + description: "Senior Code Reviewer" + instruction: | + You are a senior code reviewer with expertise in multiple languages. + + When reviewing code, check for: + 1. Code quality and readability + 2. Potential bugs and edge cases + 3. Performance issues + 4. Security vulnerabilities + 5. Best practices and design patterns + 6. Test coverage gaps + + Provide specific, actionable feedback with examples. + Rate severity: Critical, Major, Minor, Suggestion + + architect: + model: github-gpt4o + description: "Software Architect" + instruction: | + You are a software architect with experience in: + - Microservices and distributed systems + - API design (REST, GraphQL, gRPC) + - Database design and optimization + - Cloud architecture (AWS, Azure, GCP) + - Scalability and performance + + Provide architecture recommendations with: + - Clear diagrams (ASCII/Mermaid) + - Trade-off analysis + - Migration strategies + - Cost considerations + + debugger: + model: github-gpt4o + description: "Debugging Specialist" + instruction: | + You are an expert debugger who helps identify and fix issues. + + Debugging approach: + 1. Understand the expected vs actual behavior + 2. Identify potential root causes + 3. Suggest diagnostic steps + 4. Provide fix recommendations + 5. Explain how to prevent similar issues + + Ask clarifying questions when needed. + + tester: + model: github-gpt4o-mini + description: "QA and Testing Expert" + instruction: | + You are a QA engineer specializing in: + - Unit testing (Jest, pytest, Go testing) + - Integration testing + - E2E testing (Playwright, Cypress) + - Test-driven development + - Coverage analysis + + Generate comprehensive test cases and explain testing strategies. + + devops: + model: github-gpt4o-mini + description: "DevOps Engineer" + instruction: | + You are a DevOps engineer with expertise in: + - CI/CD pipelines (GitHub Actions, GitLab CI) + - Docker and Kubernetes + - Infrastructure as Code (Terraform, Pulumi) + - Monitoring and observability + - Security best practices + + Help with deployment, automation, and infrastructure. + +models: + github-gpt4o: + provider: github + model: openai/gpt-4o + max_tokens: 8192 + + github-gpt4o-mini: + provider: github + model: openai/gpt-4o-mini + max_tokens: 4096 + +providers: + github: + type: openai + base_url: https://models.inference.ai.azure.com + api_key: ${GITHUB_TOKEN} diff --git a/agents/brainsait-healthcare.yaml b/agents/brainsait-healthcare.yaml new file mode 100644 index 00000000..833ba116 --- /dev/null +++ b/agents/brainsait-healthcare.yaml @@ -0,0 +1,106 @@ +#!/usr/bin/env cagent run + +# BrainSait Healthcare Insurance Agent +# HIPAA-aware assistant for healthcare insurance analysis + +agents: + root: + model: github-gpt4o + description: "BrainSait Healthcare Insurance Analyst - Claims and Prior Authorization Assistant" + instruction: | + You are a specialized healthcare insurance AI assistant designed to help with: + - Insurance claim analysis and processing + - Prior authorization requests and documentation + - Medical coding verification (ICD-10, CPT, HCPCS) + - Coverage determination and policy interpretation + - Appeals and denial management + + IMPORTANT COMPLIANCE GUIDELINES: + - Never store or retain any PHI (Protected Health Information) + - Do not make final coverage decisions - only provide analysis + - Always recommend human review for complex cases + - Reference relevant regulations when applicable + - Maintain professional, objective tone + + You have access to tools for document analysis and research. + Use them to provide accurate, well-researched responses. + + toolsets: + - type: mcp + ref: docker:duckduckgo + instruction: | + Search for medical coding information, CMS guidelines, + payer policies, and clinical references. + + - type: mcp + ref: docker:filesystem + instruction: | + Read and analyze uploaded claim documents and EOBs. + tools: ["read_file", "list_directory"] + + commands: + analyze-claim: "Analyze this insurance claim for completeness and accuracy" + check-auth: "Review prior authorization requirements for this procedure" + appeal-help: "Help draft an appeal for this denied claim" + code-check: "Verify medical codes and suggest alternatives" + + claims_analyst: + model: github-gpt4o + description: "Claims Processing Specialist" + instruction: | + You specialize in analyzing healthcare insurance claims. + + For each claim, evaluate: + 1. Completeness of required fields + 2. Accuracy of diagnosis and procedure codes + 3. Medical necessity documentation + 4. Timely filing compliance + 5. Coordination of benefits issues + + Provide a structured analysis with recommendations. + + prior_auth_specialist: + model: github-gpt4o + description: "Prior Authorization Expert" + instruction: | + You are an expert in prior authorization requirements and processes. + + Help users by: + 1. Identifying PA requirements for specific procedures + 2. Reviewing clinical documentation for adequacy + 3. Suggesting additional documentation if needed + 4. Explaining appeal options for denials + + Reference CMS and major payer guidelines when applicable. + + coding_expert: + model: github-gpt4o-mini + description: "Medical Coding Specialist" + instruction: | + You are a certified medical coding specialist (CPC/CCS equivalent). + + Expertise areas: + - ICD-10-CM/PCS diagnosis and procedure codes + - CPT and HCPCS Level II codes + - Modifier usage and sequencing + - Medical necessity criteria + - Bundling and unbundling rules + + Always cite coding guidelines and explain your reasoning. + +models: + github-gpt4o: + provider: github + model: openai/gpt-4o + max_tokens: 8192 + + github-gpt4o-mini: + provider: github + model: openai/gpt-4o-mini + max_tokens: 4096 + +providers: + github: + type: openai + base_url: https://models.inference.ai.azure.com + api_key: ${GITHUB_TOKEN} diff --git a/awesome-ospo.html b/awesome-ospo.html new file mode 100644 index 00000000..ce4fadb4 --- /dev/null +++ b/awesome-ospo.html @@ -0,0 +1,3747 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + todogroup/awesome-ospo: Curated list of awesome tools for managing open source programs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + +
+ Skip to content + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ +
+ + + + todogroup  /   + awesome-ospo  /   + +
+
+ + + +
+ + +
+
+ Clear Command Palette +
+
+ + +
+
+ Tip: + Type # to search pull requests +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type # to search issues +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type # to search discussions +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type ! to search projects +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type @ to search teams +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type @ to search people and organizations +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type > to activate command mode +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Go to your accessibility settings to change your keyboard shortcuts +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type author:@me to search your content +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:pr to filter to pull requests +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:issue to filter to issues +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:project to filter to projects +
+
+ Type ? for help and tips +
+
+
+ +
+
+ Tip: + Type is:open to filter to open content +
+
+ Type ? for help and tips +
+
+
+ +
+ +
+
+ We’ve encountered an error and some results aren't available at this time. Type a new search or try again later. +
+
+ + No results matched your search + + + + + + + + + + +
+ + + + + Search for issues and pull requests + + # + + + + Search for issues, pull requests, discussions, and projects + + # + + + + Search for organizations, repositories, and users + + @ + + + + Search for projects + + ! + + + + Search for files + + / + + + + Activate command mode + + > + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Filter to pull requests + + # is:pr + + + + Filter to issues + + # is:issue + + + + Filter to discussions + + # is:discussion + + + + Filter to projects + + # is:project + + + + Filter to open issues, pull requests, and discussions + + # is:open + + + + + + + + + + + + + + + + +
+
+
+ +
+ + + + + + + + + + +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+ Owner avatar + + + + awesome-ospo + + + Public +
+ +
+
+ + +
+ +
+
    + + + +
  • + + +
    + + + +
    + + + +
    +
  • + +
  • +
    + Fork + 68 + Fork your own copy of todogroup/awesome-ospo + +
    + + + +
    + +
    +
    + + + + + + + Loading + + + + +
    + +
    +
    +
    +
    +
  • + +
  • + + +
    +
    +
    + + +
    + + + +
    +
    +

    Lists

    + + +
    +
    +
    + + + + + Loading + + + +
    + +
    +
    +
    +
    +
    +
    +
    + +
    + + + +
    +
    +

    Lists

    + + +
    +
    +
    + + + + + Loading + + + +
    + +
    +
    +
    +
    +
    +
  • + +
+ +
+
+ +
+
+
+
+
+ + + + + + +
+
+ + + +
+ +Fork your own copy of todogroup/awesome-ospo + +
+ +
+
+
+ + Unstar this repository + +
+ + + +
+
+

Lists

+ + +
+
+
+ + + + + Loading + + + +
+ +
+
+
+
+
+
+
+ + Star this repository + +
+ + + +
+
+

Lists

+ + +
+
+
+ + + + + Loading + + + +
+ +
+
+
+
+
+
+
+ + +
+
+

+ Curated list of awesome tools for managing open source programs +

+ + + +

License

+ + + +
+

Code of conduct

+ + +

Contributing

+ + + + + +
+
+ +
+ + Public repository + +
+ +
+
+ +
+ +
+
+ +
+ + + + +
+ Open in github.dev + Open in a new github.dev tab + Open in codespace + + + + + + +

todogroup/awesome-ospo

+
+
+ +
+
+ + + + + +

Add file

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Awesome OSS Management Awesome

+

This list identifies packages and projects that have been built by TODO Group +members or found helpful for managing open source projects and offices.

+

Code Reviews

+

Tools for managing and automating code review processes.

+
    +
  • PullApprove - Allows for fancier rules on how pull requests are approved.
  • +
  • sentinel - PR Test, review, and merge workflow bot.
  • +
  • pull-review - Assign pull request reviewers intelligently, inspired by mention-bot.
  • +
  • pull-request-size - Automatically adds GitHub labels based on the size of a pull request.
  • +
  • Pullie - GitHub App that helps with pull requests (requests reviews, links Jira tickets, reminds users about missing required file changes e.g., changelog entries).
  • +
+

Continuous Integration / Continuous Delivery

+

CI/CD platforms and tools.

+
    +
  • GitHub Actions - Automate your workflow from idea to production.
  • +
  • Jenkins - Open source automation server that provides hundreds of plugins to support building, deploying and automating any project.
  • +
  • Jenkins X - Open source CI/CD solution for modern cloud applications on Kubernetes.
  • +
  • Ortelius - Provides a central catalog of services with their deployment specs, application teams can easily consume and deploy services across cluster.
  • +
  • Screwdriver - Open source build platform designed for Continuous Delivery.
  • +
  • Spinnaker - Multi-cloud continuous delivery platform for releasing software changes with high velocity and confidence.
  • +
  • Tekton - Powerful and flexible open source framework for creating CI/CD systems, allowing developers to build, test, and deploy across cloud providers and on-premise systems.
  • +
  • Travis CI - A hosted continuous integration service used to build and test software projects hosted at GitHub and Bitbucket.
  • +
+

Contributor License Agreements / Developer Certificate of Origins

+

CLA and DCO management tools.

+
    +
  • CLA Assistant - Streamline your workflow and let CLA assistant handle the legal side of contributions to a repository for you. CLA assistant enables contributors to sign CLAs from within a pull request.
  • +
  • Dr CLA - GitHub bot for dealing with Contributor License Agreements.
  • +
  • DCO Bot - GitHub App that enforces the Developer Certificate of Origin (DCO) on Pull Requests.
  • +
  • EasyCLA - A Contributor License Agreement (CLA) service used in the Linux Foundation's LFX platform which lets project contributors read, sign, and submit contributor license agreements easily.
  • +
+

GitHub Metrics and Dashboards

+

Tools for tracking and visualizing GitHub activity.

+
    +
  • osstracker - An application that collects information about a GitHub organization and aggregates the data across all projects within that organization into a single user interface to be used by various roles within the owning organization.
  • +
  • devstats - A toolset to visualize GitHub archives using Grafana dashboards used by the Cloud Native Computing Foundation and Kubernetes.
  • +
  • MeasureOSS - A contributor relationship management system.
  • +
  • GrimoireLab - Software development analytics platform supporting more than 30 different data sources, part of CHAOSS Software project from The Linux Foundation.
  • +
  • Project Portal - Lists all InnerSource (or Open Source) projects of a company in an interactive and easy to use way. Can be used as a template for implementing the "InnerSource portal" pattern by the InnerSource Commons community.
  • +
  • Issue/PR/Discussion Metrics - A GitHub Action that searches for pull requests/issues/discussions in a repository or organization and measures several available metrics like time to close and time to first response. It calculates the metrics and writes the metrics to a Markdown file. The issues/pull requests/discussions can be filtered by using a search query.
  • +
  • Augur - A software suite for collecting and measuring structured data about OSS communities.
  • +
+

GitHub Management

+

Tools for managing GitHub organizations and repositories.

+
    +
  • opensource-management-portal - Microsoft's Open Source Portal for GitHub is a tool to help large organizations with GitHub management operations, onboarding and more. It is implemented in Node.js.
  • +
  • hubcommander - A Slack bot for GitHub organization management.
  • +
  • GitHub Settings - Uses .github/config.yml as the source of truth, and any changes to that file in the default branch will update GitHub.
  • +
  • Copybara - A tool for transforming and moving code between repositories.
  • +
  • github-org-mgmt - A few scripts for managing a GitHub organization.
  • +
  • github-org-scripts - Some helper scripts to manage GitHub organizations via API.
  • +
  • Automated GitHub Organization Invites - Host a webpage to allow people to click and receive an invite to your GitHub Organization.
  • +
  • Pepper - A tool for performing actions on GitHub repos or a single repo.
  • +
  • Grit - A tool to mirror monorepo subtrees to GitHub.
  • +
  • Sheriff - Controls and monitors organization permissions across GitHub, Slack and GSuite.
  • +
  • Mariner Issue Collector - Identify open issues across all of your dependencies.
  • +
  • Steampipe GitHub Plugin - Query GitHub Repositories, Organizations, and other resources with SQL.
  • +
  • Powerpipe GitHub Sherlock Mod - Interrogate your GitHub resource configurations to identify improvements based on best practices.
  • +
  • (Corporate) Git Proxy - Scan outgoing attempts to push to public repository and raise compliance/info-sec friendly checks before allowing the push to complete.
  • +
  • Stale Repos Action - Get a regular report of inactive repositories in your organization so that you can choose to archive or revive.
  • +
  • Forker - A GitHub Action that can automate the creation of fork repositories.
  • +
+

Governance

+

Tools for project governance and community management.

+
    +
  • Minimal Viable Governance - Currently in beta - is a repository-based approach for putting lightweight governance into free and open source projects that are run in version control systems. It provides an overall two-tier organizational governance structure for a set of free and open source projects.
  • +
+

Project Quality

+

Tools for assessing and improving project quality.

+
    +
  • OpenSSF Best Practices Badge - The Open Source Security Foundation (OpenSSF) Best Practices badge is a way for Free/Libre and Open Source Software (FLOSS) projects to show that they follow best practices. Projects can voluntarily self-certify, at no cost, by using this web application to explain how they follow each best practice. The OpenSSF Best Practices Badge is inspired by the many badges available to projects on GitHub. Consumers of the badge can quickly assess which FLOSS projects are following best practices and as a result are more likely to produce higher-quality secure software.
  • +
  • Fosstars - A framework for defining and calculating ratings for open source projects.
  • +
  • RepoLinter - Lint open source repositories for common issues.
  • +
  • Linguist - Identify the programming languages used in a project.
  • +
  • repo-scaffolding - Scaffolding tools for creating and maintaining projects based on Twitter Open Source standards and best practices.
  • +
  • Repo Health Check - Analyze a project: How are the maintainers doing?
  • +
+

Supply Chain Trust

+

Tools/frameworks for managing software supply chain security.

+
    +
  • OpenChain Conformance - The OpenChain Specification is a way for companies using Free/Libre and Open Source Software (FLOSS) to show that they meet the key requirements for quality compliance programs. Companies can voluntarily self-certify, at no cost, by using this web application.
  • +
+

Licensing

+

Tools for managing and tracking open source licenses.

+
    +
  • SPDX - Set of standards for communicating the components, licenses and copyright associated with a software package.
  • +
  • LicenseFinder - Find licenses for your project's dependencies.
  • +
  • ScanCode toolkit - Scan code for licenses, copyright and dependencies.
  • +
  • FOSSology - Scan code for license, copyright and export control information.
  • +
  • Licensee - Identify a project's license file.
  • +
  • askalono - A library and command-line tool to help detect license texts. It's designed to be fast, accurate, and to support a wide variety of license texts.
  • +
  • License Classifier - A library and set of tools that can analyze text to determine what type of license it contains.
  • +
  • OSS Review Toolkit - Enables highly automated and customizable open source compliance checks od the source code and dependencies of a project by scanning it, downloading its sources, reporting any errors and violations against user-defined rules, and by creating third-party attribution documentation.
  • +
  • fossa-cli - Fast, portable and reliable dependency analysis for any codebase.
  • +
  • Licensed - A Ruby gem to cache and verify the licenses of dependencies.
  • +
  • LicensePlist - A command-line tool that automatically generates a Plist of all your dependencies, including files added manually (specified by YAML config file) or using Carthage or CocoaPods.
  • +
  • dpkg-licenses - A command line tool which lists the licenses of all installed packages in a Debian-based system (like Ubuntu).
  • +
  • FOSSID - A comprehensive commercial scanner for licenses and vulnerabilities. Knowledgebase covers 78M+ repositories and 600B+ snippets. Includes detailed snippet scanning to detect the license on fragments and copied/pasted code, even if the open source license is not explicitly or correctly declared.
  • +
  • DependencyTrack - An intelligent component analysis platform that allows organizations to identify and reduce risk in the software supply chain.
  • +
  • ScanOSS - Scan your codebase for snippets and plagerism from large knowledge base of open source projects. Designed to integrate with CI/CD and modern IDEs, to "start left" to do continuous validation instead of one report at the end. Product itself is fully open source.
  • +
  • TLDRLegal - Summarizes the most common open source licenses in plain English. Provides a quick reference for what a user can, cannot, and must do according to the license terms.
  • +
  • Choose A License - Recommends an open source license based on the collaboration style and intended use of a project. The site's appendix provides a helpful birds-eye view of terms across the most common licenses.
  • +
  • ClearlyDefined - An open source project and a free service that provides a cached copy of licensing metadata for software components through a simple API. Organizations are be able to contribute back any missing or wrongly identified licensing metadata, helping to create a global database that is accurate for the benefit of all, improving compliance and security across the whole software supply chain.
  • +
+

Localization and Internationalization

+

Tools for managing translations and i18n.

+
    +
  • zanata - Web-based system for translators to translate documentation and software online using a web browser.
  • +
  • Weblate - Web-based translation management system.
  • +
  • Respresso - Multiplatform localization converter for iOS (.strings + Objective-C getters), Android (strings.xml) and Web (.json).
  • +
+

Websites and Documentation

+

Tools for creating and managing project websites and documentation.

+
    +
  • Docusaurus - React-based static site generator, specifically developed to more easily help create and maintain open source websites.
  • +
  • GatsbyJS - Site generator that allows you to build fast websites and apps with React.
  • +
  • VuePress - Vue-based static site generator, optimized for writing technical documentation.
  • +
+

Security

+

Security scanning and vulnerability management tools.

+
    +
  • Eclipse Steady - Helps to discover, assess and mitigate known vulnerabilities in Java and Python projects. Formerly known as "Vulnerability Assessement Tool" (Vulas).
  • +
+

In-Kind Donations

+

Resources for in-kind donations and support programs.

+

The following organizations have formal or informal programs for offering in-kind donations to free and open source projects or foundations.

+
    +
  • AWS - A program started in 2019 to provide promotional credits to open source projects. Details are in this blog post (Last Updated: April 14, 2021)
  • +
  • Azure Credits - This program grants Azure credits to open source projects, which developers can use for testing, storage, or other development.
  • +
+

Content License

+

License: CC BY-SA 4.0 © Contributors 2016-2021

+
+
+ + + +
+
+ +
+
+
+
+

About

+ +

+ Curated list of awesome tools for managing open source programs +

+
+ + + todogroup.org + +
+ +

Topics

+ + +

Resources

+ + + +

License

+ + + +

Code of conduct

+ + + +

Contributing

+ + + + + + + + + + + + +

Stars

+ + +

Watchers

+ + +

Forks

+ + + + +
+ +
+
+ + +
+
+

+ Releases

+ +
No releases published
+ +
+
+ + + +
+
+ +

+ Packages +

+ + +
+ No packages published
+
+ + + +
+
+ + + + + +
+
+

+ Contributors + 39

+ + + + +
    +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
  • +
    +
  • +
+ + +
+ + +
+
+ + + +
+
+ +
+ +
+ + +
+ +
+ + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + diff --git a/cmd/api/main.go b/cmd/api/main.go new file mode 100644 index 00000000..40194a4c --- /dev/null +++ b/cmd/api/main.go @@ -0,0 +1,63 @@ +// Package main provides the HTTP API server for BrainSait AI Platform +package main + +import ( + "context" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/github/gh-models/internal/api" + "github.com/github/gh-models/internal/billing" +) + +func main() { + // Load configuration + cfg := api.LoadConfig() + + // Initialize billing service + billingService := billing.NewService(cfg.StripeSecretKey, cfg.StripeWebhookSecret) + + // Initialize API server + server := api.NewServer(cfg, billingService) + + // Setup routes + router := server.SetupRoutes() + + // Create HTTP server + srv := &http.Server{ + Addr: ":" + cfg.Port, + Handler: router, + ReadTimeout: 30 * time.Second, + WriteTimeout: 60 * time.Second, + IdleTimeout: 120 * time.Second, + } + + // Start server in goroutine + go func() { + log.Printf("🚀 BrainSait API Server starting on port %s", cfg.Port) + log.Printf("📍 Environment: %s", cfg.Environment) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("Failed to start server: %v", err) + } + }() + + // Graceful shutdown + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + + log.Println("⏳ Shutting down server...") + + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + if err := srv.Shutdown(ctx); err != nil { + log.Fatalf("Server forced to shutdown: %v", err) + } + + log.Println("✅ Server stopped gracefully") +} diff --git a/compose-agents/README.md b/compose-agents/README.md new file mode 100644 index 00000000..485fff86 --- /dev/null +++ b/compose-agents/README.md @@ -0,0 +1,245 @@ +# BrainSait Compose Agents + +Multi-agent AI systems built with [Docker Compose for Agents](https://github.com/docker/compose-for-agents). + +## Overview + +BrainSait provides pre-built, domain-specific AI agent systems that run locally using Docker Model Runner or in the cloud with OpenAI-compatible APIs. + +## Available Agent Systems + +| Agent System | Description | Framework | Models | +|--------------|-------------|-----------|--------| +| [Arabic Assistant](./arabic-assistant/) | Arabic language translation, writing, and research | Google ADK | qwen3 | +| [Healthcare Insurance](./healthcare-insurance/) | Claims analysis, PA, coding, appeals | Google ADK | qwen3 | +| [Developer Assistant](./developer-assistant/) | Code review, architecture, debugging, testing, DevOps | Google ADK | qwen3 | +| [SQL Analyst](./sql-analyst/) | Natural language to SQL with data analysis | LangGraph | qwen3 | + +## Prerequisites + +- **Docker Desktop 4.43.0+** with Model Runner enabled +- **GPU recommended** for running local models (or use [Docker Offload](https://www.docker.com/products/docker-offload/)) +- **MCP Toolkit** enabled in Docker Desktop settings + +## Quick Start + +### 1. Choose an Agent System + +```bash +cd compose-agents/developer-assistant +``` + +### 2. Configure Secrets (if required) + +```bash +# Copy example env file +cp mcp.env.example .mcp.env + +# Edit with your tokens +nano .mcp.env +``` + +### 3. Start the Agent System + +```bash +docker compose up --build +``` + +### 4. Access the Web Interface + +Open [http://localhost:8080](http://localhost:8080) in your browser. + +## Using OpenAI Instead of Local Models + +To use OpenAI models instead of running locally: + +1. Create a secret file: + +```bash +echo "sk-your-openai-key" > secret.openai-api-key +``` + +2. Start with OpenAI configuration: + +```bash +docker compose -f compose.yaml -f compose.openai.yaml up +``` + +## Using GitHub Models + +To use GitHub Models as the LLM provider: + +```bash +# Set your GitHub token +export GITHUB_TOKEN=$(gh auth token) + +# Start with GitHub Models +docker compose -f compose.yaml -f compose.github.yaml up +``` + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ User Interface (Port 8080) │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Agent Framework │ +│ (Google ADK / LangGraph / CrewAI) │ +└─────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ Agent 1 │ │ Agent 2 │ │ Agent N │ + └──────────┘ └──────────┘ └──────────┘ + │ │ │ + └───────────────┼───────────────┘ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ MCP Gateway │ +│ (Tool orchestration & security) │ +└─────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ DuckDuck │ │ GitHub │ │ Postgres │ + │ Go │ │ Official │ │ MCP │ + └──────────┘ └──────────┘ └──────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Docker Model Runner │ +│ (Local LLM inference engine) │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Creating Custom Agent Systems + +### 1. Create Directory Structure + +```bash +mkdir -p my-agent/agents +``` + +### 2. Create Agent Definition + +```python +# my-agent/agents/agent.py +from google.adk.agents import Agent + +root_agent = Agent( + name="my_agent", + description="My custom agent", + instruction="""You are a helpful assistant...""", + model="qwen3", +) +``` + +### 3. Create Compose File + +```yaml +# my-agent/compose.yaml +services: + agent: + build: . + ports: + - 8080:8080 + environment: + - MCP_SERVER_URL=http://mcp-gateway:8811/sse + depends_on: + - mcp-gateway + models: + qwen3: + endpoint_var: MODEL_RUNNER_URL + model_var: MODEL_RUNNER_MODEL + + mcp-gateway: + image: docker/mcp-gateway:latest + use_api_socket: true + command: + - --transport=sse + - --servers=duckduckgo + +models: + qwen3: + model: ai/qwen3:14B-Q6_K +``` + +### 4. Create Dockerfile + +```dockerfile +FROM python:3.12-slim +WORKDIR /app +RUN pip install google-adk httpx uvicorn +COPY agents/ ./agents/ +CMD ["python", "-m", "google.adk.cli", "web", "--agents-dir", "agents", "--host", "0.0.0.0", "--port", "8080"] +``` + +### 5. Run Your Agent + +```bash +cd my-agent +docker compose up --build +``` + +## MCP Tools Reference + +| Server | Tools | Description | +|--------|-------|-------------| +| duckduckgo | search, fetch_content | Web search | +| github-official | read_file, list_repos, search_code | GitHub integration | +| filesystem | read_file, write_file, list_directory | File operations | +| postgres | query | PostgreSQL database | +| brave | search | Brave search engine | +| wikipedia-mcp | search, get_article | Wikipedia access | + +## Integration with BrainSait CLI + +These agent systems complement the `gh models` CLI: + +```bash +# Generate tests for your prompts +gh models generate my-prompt.yml + +# Evaluate prompt performance +gh models eval my-prompt.yml + +# Run inference +gh models run my-prompt.yml +``` + +## Troubleshooting + +### Model Runner Not Starting + +Ensure Docker Desktop has Model Runner enabled: + +1. Open Docker Desktop settings +2. Go to "Features in development" +3. Enable "Docker Model Runner" + +### Out of Memory + +Reduce context size in compose.yaml: + +```yaml +models: + qwen3: + model: ai/qwen3:4B-Q4_0 # Smaller model + context_size: 4096 # Smaller context +``` + +### MCP Gateway Connection Failed + +Check that MCP Toolkit is enabled in Docker Desktop: + +1. Settings → Features in development → Enable MCP Toolkit +2. Restart Docker Desktop + +## License + +Apache 2.0 OR MIT (dual-licensed) diff --git a/compose-agents/arabic-assistant/Dockerfile b/compose-agents/arabic-assistant/Dockerfile new file mode 100644 index 00000000..c066adbd --- /dev/null +++ b/compose-agents/arabic-assistant/Dockerfile @@ -0,0 +1,20 @@ +# BrainSait Arabic Assistant Agent +FROM python:3.12-slim + +WORKDIR /app + +# Install dependencies +RUN pip install --no-cache-dir \ + google-adk \ + httpx \ + uvicorn + +# Copy agent code +COPY agents/ ./agents/ + +# Set environment +ENV PYTHONUNBUFFERED=1 +ENV LANGUAGE=ar + +# Run the agent +CMD ["python", "-m", "google.adk.cli", "web", "--agents-dir", "agents", "--host", "0.0.0.0", "--port", "8080"] diff --git a/compose-agents/arabic-assistant/agents/__init__.py b/compose-agents/arabic-assistant/agents/__init__.py new file mode 100644 index 00000000..d327e772 --- /dev/null +++ b/compose-agents/arabic-assistant/agents/__init__.py @@ -0,0 +1,4 @@ +# BrainSait Arabic Assistant Agents +from .agent import root_agent + +__all__ = ["root_agent"] diff --git a/compose-agents/arabic-assistant/agents/agent.py b/compose-agents/arabic-assistant/agents/agent.py new file mode 100644 index 00000000..5e0ffc0d --- /dev/null +++ b/compose-agents/arabic-assistant/agents/agent.py @@ -0,0 +1,75 @@ +"""BrainSait Arabic Language Assistant - Multi-Agent System""" + +from google.adk.agents import SequentialAgent, Agent + +# Arabic Translator Agent +translator_agent = Agent( + name="translator", + description="Translates text between Arabic and English with cultural context", + instruction="""أنت مترجم محترف متخصص في الترجمة بين العربية والإنجليزية. + +مسؤولياتك: +- ترجمة النصوص مع الحفاظ على المعنى والسياق الثقافي +- التعامل مع اللهجات العربية المختلفة (المصرية، الخليجية، الشامية، المغاربية) +- توضيح المصطلحات الثقافية عند الحاجة +- الحفاظ على الأسلوب والنبرة الأصلية للنص + +قواعد الترجمة: +1. استخدم العربية الفصحى المعاصرة +2. احترم السياق الثقافي والديني +3. وضح المصطلحات التي لا يمكن ترجمتها مباشرة +4. قدم بدائل للعبارات الاصطلاحية""", + model="qwen3", +) + +# Arabic Content Writer Agent +writer_agent = Agent( + name="writer", + description="Creates high-quality Arabic content for various purposes", + instruction="""أنت كاتب محتوى عربي محترف. + +تخصصاتك: +- كتابة المقالات والتقارير +- إنشاء محتوى تسويقي +- صياغة الرسائل الرسمية والتجارية +- تحرير وتدقيق النصوص العربية + +أسلوبك: +1. لغة عربية سليمة وفصيحة +2. أسلوب واضح ومباشر +3. مراعاة الجمهور المستهدف +4. الالتزام بقواعد الكتابة العربية""", + model="qwen3", +) + +# Arabic Research Agent +researcher_agent = Agent( + name="researcher", + description="Researches topics and provides Arabic summaries", + instruction="""أنت باحث متخصص في جمع وتحليل المعلومات. + +مهامك: +- البحث عن المعلومات من مصادر موثوقة +- تلخيص المحتوى باللغة العربية +- التحقق من صحة المعلومات +- تقديم المراجع والمصادر + +منهجيتك: +1. استخدم مصادر متعددة ومتنوعة +2. ميز بين الحقائق والآراء +3. قدم المعلومات بشكل منظم +4. أشر إلى أي تحفظات أو قيود""", + model="qwen3", +) + +# Main Arabic Assistant - Sequential Agent +arabic_assistant = SequentialAgent( + name="arabic_assistant", + description=( + "مساعد عربي شامل يجمع بين الترجمة والكتابة والبحث " + "لخدمة المستخدمين الناطقين بالعربية" + ), + sub_agents=[translator_agent, writer_agent, researcher_agent], +) + +root_agent = arabic_assistant diff --git a/compose-agents/arabic-assistant/compose.github.yaml b/compose-agents/arabic-assistant/compose.github.yaml new file mode 100644 index 00000000..5f9b7ef1 --- /dev/null +++ b/compose-agents/arabic-assistant/compose.github.yaml @@ -0,0 +1,12 @@ +# GitHub Models Configuration +# Use this with: docker compose -f compose.yaml -f compose.github.yaml up + +services: + arabic-agent: + environment: + - MODEL_RUNNER_URL=https://models.inference.ai.azure.com + - MODEL_RUNNER_MODEL=openai/gpt-4o-mini + - GITHUB_TOKEN=${GITHUB_TOKEN} + +# Override to use GitHub Models instead of local Docker Model Runner +# No local model needed when using GitHub Models API diff --git a/compose-agents/arabic-assistant/compose.yaml b/compose-agents/arabic-assistant/compose.yaml new file mode 100644 index 00000000..bda5bae0 --- /dev/null +++ b/compose-agents/arabic-assistant/compose.yaml @@ -0,0 +1,34 @@ +# BrainSait Arabic Language Assistant +# Multi-agent system for Arabic speakers using Docker Model Runner + +services: + arabic-agent: + build: + context: . + ports: + - 8080:8080 + environment: + - MCP_SERVER_URL=http://mcp-gateway:8811/sse + - LANGUAGE=ar + - CULTURE=arab + depends_on: + - mcp-gateway + models: + qwen3: + endpoint_var: MODEL_RUNNER_URL + model_var: MODEL_RUNNER_MODEL + + mcp-gateway: + image: docker/mcp-gateway:latest + use_api_socket: true + command: + - --transport=sse + - --servers=duckduckgo,wikipedia-mcp + - --tools=search,fetch_content + +models: + qwen3: + model: ai/qwen3:14B-Q6_K + context_size: 8192 + runtime_flags: + - --no-prefill-assistant diff --git a/compose-agents/developer-assistant/Dockerfile b/compose-agents/developer-assistant/Dockerfile new file mode 100644 index 00000000..efa13ddc --- /dev/null +++ b/compose-agents/developer-assistant/Dockerfile @@ -0,0 +1,25 @@ +# BrainSait Developer Assistant +FROM python:3.12-slim + +WORKDIR /app + +# Install system dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Install Python dependencies +RUN pip install --no-cache-dir \ + google-adk \ + httpx \ + uvicorn \ + gitpython + +# Copy agent code +COPY agents/ ./agents/ + +# Set environment +ENV PYTHONUNBUFFERED=1 + +# Run the agent +CMD ["python", "-m", "google.adk.cli", "web", "--agents-dir", "agents", "--host", "0.0.0.0", "--port", "8080"] diff --git a/compose-agents/developer-assistant/agents/__init__.py b/compose-agents/developer-assistant/agents/__init__.py new file mode 100644 index 00000000..b47a1801 --- /dev/null +++ b/compose-agents/developer-assistant/agents/__init__.py @@ -0,0 +1,4 @@ +# BrainSait Developer Assistant Agents +from .agent import root_agent + +__all__ = ["root_agent"] diff --git a/compose-agents/developer-assistant/agents/agent.py b/compose-agents/developer-assistant/agents/agent.py new file mode 100644 index 00000000..f80f1ce0 --- /dev/null +++ b/compose-agents/developer-assistant/agents/agent.py @@ -0,0 +1,306 @@ +"""BrainSait Developer Assistant - Multi-Agent System""" + +from google.adk.agents import SequentialAgent, Agent + +# Code Reviewer Agent +code_reviewer = Agent( + name="code_reviewer", + description="Senior code reviewer for quality, security, and best practices", + instruction="""You are a senior code reviewer with expertise in multiple programming languages. + +REVIEW FOCUS AREAS: +1. Code Quality + - Clean code principles (SOLID, DRY, KISS) + - Naming conventions and readability + - Function/method complexity + - Error handling patterns + +2. Security + - Input validation + - SQL injection prevention + - XSS vulnerabilities + - Authentication/authorization issues + - Sensitive data exposure + - Dependency vulnerabilities + +3. Performance + - Algorithm efficiency + - Memory usage + - Database query optimization + - Caching opportunities + - Async/concurrent patterns + +4. Testing + - Test coverage + - Test quality and assertions + - Edge cases and error scenarios + - Mock usage + +REVIEW OUTPUT FORMAT: +``` +## Code Review Summary + +### Critical Issues 🔴 +- [Issue description with line reference] + +### Warnings ⚠️ +- [Warning description] + +### Suggestions 💡 +- [Improvement suggestions] + +### Positive Aspects ✅ +- [Good practices observed] + +### Recommended Actions +1. [Action item] +```""", + model="qwen3", +) + +# Software Architect Agent +architect = Agent( + name="architect", + description="Software architect for system design and architecture decisions", + instruction="""You are a software architect with expertise in system design. + +ARCHITECTURE RESPONSIBILITIES: +1. System Design + - Microservices vs monolith decisions + - Service boundaries and communication + - Data flow and state management + - API design (REST, GraphQL, gRPC) + +2. Technology Selection + - Language and framework recommendations + - Database choices (SQL, NoSQL, NewSQL) + - Message queues and event systems + - Cloud services and infrastructure + +3. Scalability & Reliability + - Horizontal vs vertical scaling + - Load balancing strategies + - Caching layers + - Disaster recovery planning + +4. Design Patterns + - Creational patterns (Factory, Builder, Singleton) + - Structural patterns (Adapter, Facade, Proxy) + - Behavioral patterns (Observer, Strategy, Command) + - Architectural patterns (MVC, CQRS, Event Sourcing) + +OUTPUT FORMAT: +``` +## Architecture Recommendation + +### Problem Statement +[Summary of the design challenge] + +### Proposed Solution +[Architecture diagram description] + +### Components +- [Component 1]: [Purpose] +- [Component 2]: [Purpose] + +### Trade-offs +- Pros: [Benefits] +- Cons: [Drawbacks] + +### Implementation Roadmap +1. [Phase 1] +2. [Phase 2] +```""", + model="qwen3", +) + +# Debugging Specialist Agent +debugger = Agent( + name="debugger", + description="Debugging specialist for troubleshooting and fixing issues", + instruction="""You are a debugging specialist with expertise in troubleshooting. + +DEBUGGING METHODOLOGY: +1. Reproduce the Issue + - Identify exact steps to reproduce + - Note environment conditions + - Gather error messages and stack traces + +2. Isolate the Problem + - Binary search through code changes + - Add logging/breakpoints strategically + - Check recent changes in git history + - Test with minimal reproduction case + +3. Analyze Root Cause + - Trace execution flow + - Examine variable states + - Check boundary conditions + - Review external dependencies + +4. Fix and Verify + - Implement targeted fix + - Add regression tests + - Verify in all affected scenarios + - Document the fix + +COMMON BUG PATTERNS: +- Off-by-one errors +- Null/undefined references +- Race conditions +- Memory leaks +- Incorrect type handling +- API contract violations + +OUTPUT FORMAT: +``` +## Bug Analysis + +### Issue Description +[What's happening vs expected behavior] + +### Root Cause +[Identified cause with evidence] + +### Suggested Fix +```language +[Code fix] +``` + +### Testing Recommendation +[How to verify the fix] +```""", + model="qwen3", +) + +# Test Engineer Agent +tester = Agent( + name="tester", + description="QA engineer for test generation and testing strategies", + instruction="""You are a QA engineer with expertise in software testing. + +TESTING RESPONSIBILITIES: +1. Unit Testing + - Test individual functions/methods + - Mock external dependencies + - Cover edge cases and error paths + - Achieve high code coverage + +2. Integration Testing + - Test component interactions + - API endpoint testing + - Database integration tests + - Service-to-service communication + +3. E2E Testing + - User journey testing + - Cross-browser compatibility + - Mobile responsiveness + - Performance under load + +4. Test Design + - Boundary value analysis + - Equivalence partitioning + - Decision table testing + - State transition testing + +TESTING BEST PRACTICES: +- AAA pattern (Arrange, Act, Assert) +- One assertion per test (when practical) +- Descriptive test names +- Independent and isolated tests +- Fast execution time + +OUTPUT FORMAT: +``` +## Test Plan + +### Test Scope +[What's being tested] + +### Test Cases +| ID | Description | Input | Expected Output | +|----|-------------|-------|-----------------| +| T1 | [Name] | [In] | [Out] | + +### Generated Test Code +```language +[Test implementation] +``` +```""", + model="qwen3", +) + +# DevOps Engineer Agent +devops = Agent( + name="devops", + description="DevOps engineer for CI/CD, deployment, and infrastructure", + instruction="""You are a DevOps engineer with expertise in CI/CD and infrastructure. + +DEVOPS RESPONSIBILITIES: +1. CI/CD Pipelines + - Build automation + - Test automation + - Deployment automation + - Release management + +2. Infrastructure as Code + - Terraform/Pulumi + - Docker/Kubernetes + - Cloud formation + - Ansible/Chef/Puppet + +3. Monitoring & Observability + - Metrics collection (Prometheus) + - Log aggregation (ELK, Loki) + - Distributed tracing (Jaeger) + - Alerting (PagerDuty, OpsGenie) + +4. Security & Compliance + - Secret management + - Network security + - Access control + - Compliance automation + +DEPLOYMENT STRATEGIES: +- Blue-green deployment +- Canary releases +- Rolling updates +- Feature flags + +OUTPUT FORMAT: +``` +## DevOps Recommendation + +### Current State +[Assessment of current setup] + +### Proposed Solution + +#### Pipeline Configuration +```yaml +[CI/CD config] +``` + +#### Infrastructure Code +```hcl +[IaC snippet] +``` + +### Monitoring Setup +[Observability recommendations] +```""", + model="qwen3", +) + +# Main Developer Assistant - Sequential Agent +developer_assistant = SequentialAgent( + name="developer_assistant", + description=( + "Comprehensive developer assistant combining code review, architecture, " + "debugging, testing, and DevOps expertise for complete software development lifecycle support" + ), + sub_agents=[code_reviewer, architect, debugger, tester, devops], +) + +root_agent = developer_assistant diff --git a/compose-agents/developer-assistant/compose.github.yaml b/compose-agents/developer-assistant/compose.github.yaml new file mode 100644 index 00000000..9f71446f --- /dev/null +++ b/compose-agents/developer-assistant/compose.github.yaml @@ -0,0 +1,9 @@ +# GitHub Models Configuration +# Use with: docker compose -f compose.yaml -f compose.github.yaml up + +services: + developer-agent: + environment: + - MODEL_RUNNER_URL=https://models.inference.ai.azure.com + - MODEL_RUNNER_MODEL=openai/gpt-4o-mini + - GITHUB_TOKEN=${GITHUB_TOKEN} diff --git a/compose-agents/developer-assistant/compose.yaml b/compose-agents/developer-assistant/compose.yaml new file mode 100644 index 00000000..7e8ba2c6 --- /dev/null +++ b/compose-agents/developer-assistant/compose.yaml @@ -0,0 +1,42 @@ +# BrainSait Developer Assistant +# Multi-agent system for software development + +services: + developer-agent: + build: + context: . + ports: + - 8080:8080 + environment: + - MCP_SERVER_URL=http://mcp-gateway:8811/sse + depends_on: + - mcp-gateway + volumes: + - ./workspace:/workspace + models: + qwen3: + endpoint_var: MODEL_RUNNER_URL + model_var: MODEL_RUNNER_MODEL + + mcp-gateway: + image: docker/mcp-gateway:latest + use_api_socket: true + command: + - --transport=sse + - --servers=github-official,filesystem,duckduckgo + - --tools=search,read_file,write_file,list_directory + secrets: + - github-token + environment: + - GITHUB_PERSONAL_ACCESS_TOKEN_FILE=/run/secrets/github-token + +models: + qwen3: + model: ai/qwen3:14B-Q6_K + context_size: 32768 + runtime_flags: + - --no-prefill-assistant + +secrets: + github-token: + file: ./github_token diff --git a/compose-agents/developer-assistant/github_token b/compose-agents/developer-assistant/github_token new file mode 100644 index 00000000..1aca2697 --- /dev/null +++ b/compose-agents/developer-assistant/github_token @@ -0,0 +1,2 @@ +# Add your GitHub personal access token here +# Get one from: https://github.com/settings/tokens diff --git a/compose-agents/healthcare-insurance/Dockerfile b/compose-agents/healthcare-insurance/Dockerfile new file mode 100644 index 00000000..2f4ea540 --- /dev/null +++ b/compose-agents/healthcare-insurance/Dockerfile @@ -0,0 +1,21 @@ +# BrainSait Healthcare Insurance Analyst +FROM python:3.12-slim + +WORKDIR /app + +# Install dependencies +RUN pip install --no-cache-dir \ + google-adk \ + httpx \ + uvicorn \ + psycopg2-binary + +# Copy agent code +COPY agents/ ./agents/ + +# Set environment +ENV PYTHONUNBUFFERED=1 +ENV HIPAA_COMPLIANT=true + +# Run the agent +CMD ["python", "-m", "google.adk.cli", "web", "--agents-dir", "agents", "--host", "0.0.0.0", "--port", "8080"] diff --git a/compose-agents/healthcare-insurance/agents/__init__.py b/compose-agents/healthcare-insurance/agents/__init__.py new file mode 100644 index 00000000..78afab98 --- /dev/null +++ b/compose-agents/healthcare-insurance/agents/__init__.py @@ -0,0 +1,4 @@ +# BrainSait Healthcare Insurance Agents +from .agent import root_agent + +__all__ = ["root_agent"] diff --git a/compose-agents/healthcare-insurance/agents/agent.py b/compose-agents/healthcare-insurance/agents/agent.py new file mode 100644 index 00000000..3c04d4dd --- /dev/null +++ b/compose-agents/healthcare-insurance/agents/agent.py @@ -0,0 +1,169 @@ +"""BrainSait Healthcare Insurance Analyst - Multi-Agent System""" + +from google.adk.agents import SequentialAgent, Agent + +# Claims Analyst Agent +claims_analyst = Agent( + name="claims_analyst", + description="Analyzes healthcare insurance claims for completeness and accuracy", + instruction="""You are a healthcare insurance claims analyst with expertise in: + +RESPONSIBILITIES: +- Review claims for completeness and required documentation +- Verify patient eligibility and coverage +- Identify potential issues before processing +- Ensure compliance with payer guidelines + +HIPAA COMPLIANCE: +- Never expose PHI (Protected Health Information) unnecessarily +- Use minimum necessary standard for all data access +- Log all access to patient records +- Report any potential HIPAA violations + +CLAIM REVIEW CHECKLIST: +1. Patient demographics and eligibility verification +2. Provider credentialing status +3. Service codes (CPT, ICD-10, HCPCS) validation +4. Medical necessity documentation +5. Prior authorization requirements +6. Timely filing compliance +7. Coordination of benefits check + +OUTPUT FORMAT: +- Claim Status: [APPROVED/PENDING/DENIED/NEEDS_REVIEW] +- Issues Found: [List any problems] +- Required Actions: [Next steps] +- Compliance Notes: [Any regulatory concerns]""", + model="qwen3", +) + +# Prior Authorization Specialist Agent +prior_auth_specialist = Agent( + name="prior_auth_specialist", + description="Handles prior authorization requirements and documentation", + instruction="""You are a prior authorization specialist with expertise in: + +RESPONSIBILITIES: +- Determine prior authorization requirements for services +- Gather and organize required documentation +- Track authorization status and deadlines +- Communicate with providers about PA requirements + +PA WORKFLOW: +1. Check if service requires prior authorization +2. Verify current authorization status +3. Identify required clinical documentation +4. Review medical necessity criteria +5. Submit authorization request +6. Track and follow up on pending requests + +COMMON PA REQUIREMENTS: +- Advanced imaging (MRI, CT, PET) +- Specialty medications +- Durable medical equipment +- Surgical procedures +- Specialty referrals +- Genetic testing + +OUTPUT FORMAT: +- PA Required: [YES/NO] +- Authorization Status: [APPROVED/PENDING/DENIED/NOT_SUBMITTED] +- Required Documentation: [List] +- Deadline: [Date if applicable] +- Next Steps: [Actions needed]""", + model="qwen3", +) + +# Medical Coding Expert Agent +coding_expert = Agent( + name="coding_expert", + description="Validates and optimizes medical coding for claims", + instruction="""You are a certified medical coding expert with expertise in: + +CODING SYSTEMS: +- ICD-10-CM/PCS (Diagnosis codes) +- CPT (Procedure codes) +- HCPCS (Healthcare Common Procedure Coding System) +- DRG (Diagnosis Related Groups) +- Revenue codes + +RESPONSIBILITIES: +- Validate code accuracy and specificity +- Identify coding errors and omissions +- Suggest compliant code optimizations +- Ensure documentation supports codes +- Check modifier usage + +CODING COMPLIANCE: +- Never suggest upcoding or unbundling +- Ensure codes match documentation +- Follow LCD/NCD requirements +- Apply correct modifiers +- Check bundling edits (NCCI) + +OUTPUT FORMAT: +- Code Review Status: [VALID/INVALID/NEEDS_REVIEW] +- Issues Found: [List coding problems] +- Suggested Corrections: [If applicable] +- Documentation Notes: [Support requirements]""", + model="qwen3", +) + +# Appeals Specialist Agent +appeals_specialist = Agent( + name="appeals_specialist", + description="Handles claim denials and appeals process", + instruction="""You are a healthcare appeals specialist with expertise in: + +RESPONSIBILITIES: +- Review denial reasons and determine appeal strategy +- Gather supporting documentation for appeals +- Draft appeal letters with proper argumentation +- Track appeal deadlines and status +- Analyze denial patterns for process improvement + +APPEAL LEVELS: +1. First-level appeal (internal review) +2. Second-level appeal (external review) +3. Independent external review +4. State insurance department complaint +5. Legal action (if necessary) + +COMMON DENIAL REASONS: +- Medical necessity not established +- Prior authorization not obtained +- Service not covered +- Out-of-network provider +- Timely filing exceeded +- Duplicate claim +- Coding errors + +APPEAL LETTER STRUCTURE: +1. Patient and claim identification +2. Denial reason being appealed +3. Medical necessity justification +4. Supporting clinical documentation +5. Relevant policy citations +6. Requested action + +OUTPUT FORMAT: +- Appeal Recommendation: [YES/NO] +- Appeal Level: [1st/2nd/External] +- Key Arguments: [List] +- Required Documentation: [List] +- Deadline: [Date]""", + model="qwen3", +) + +# Main Healthcare Analyst - Sequential Agent +healthcare_analyst = SequentialAgent( + name="healthcare_analyst", + description=( + "Comprehensive healthcare insurance analyst that combines claims analysis, " + "prior authorization, medical coding, and appeals expertise to provide " + "complete claim lifecycle management" + ), + sub_agents=[claims_analyst, prior_auth_specialist, coding_expert, appeals_specialist], +) + +root_agent = healthcare_analyst diff --git a/compose-agents/healthcare-insurance/compose.github.yaml b/compose-agents/healthcare-insurance/compose.github.yaml new file mode 100644 index 00000000..fd1cf351 --- /dev/null +++ b/compose-agents/healthcare-insurance/compose.github.yaml @@ -0,0 +1,9 @@ +# GitHub Models Configuration +# Use with: docker compose -f compose.yaml -f compose.github.yaml up + +services: + healthcare-agent: + environment: + - MODEL_RUNNER_URL=https://models.inference.ai.azure.com + - MODEL_RUNNER_MODEL=openai/gpt-4o-mini + - GITHUB_TOKEN=${GITHUB_TOKEN} diff --git a/compose-agents/healthcare-insurance/compose.yaml b/compose-agents/healthcare-insurance/compose.yaml new file mode 100644 index 00000000..d67a1a9a --- /dev/null +++ b/compose-agents/healthcare-insurance/compose.yaml @@ -0,0 +1,58 @@ +# BrainSait Healthcare Insurance Analyst +# Multi-agent system for healthcare claims processing + +services: + healthcare-agent: + build: + context: . + ports: + - 8080:8080 + environment: + - MCP_SERVER_URL=http://mcp-gateway:8811/sse + - HIPAA_COMPLIANT=true + depends_on: + - mcp-gateway + - database + models: + qwen3: + endpoint_var: MODEL_RUNNER_URL + model_var: MODEL_RUNNER_MODEL + + database: + image: postgres:16-alpine + environment: + POSTGRES_USER: healthcare + POSTGRES_PASSWORD: secure_password + POSTGRES_DB: claims + volumes: + - healthcare_data:/var/lib/postgresql/data + healthcheck: + test: [CMD-SHELL, pg_isready -U healthcare -d claims] + interval: 5s + timeout: 3s + retries: 10 + + mcp-gateway: + image: docker/mcp-gateway:latest + use_api_socket: true + command: + - --transport=sse + - --secrets=/run/secrets/database-url + - --servers=postgres,duckduckgo + - --tools=query,search + secrets: + - database-url + +models: + qwen3: + model: ai/qwen3:14B-Q6_K + context_size: 16384 + runtime_flags: + - --no-prefill-assistant + +secrets: + database-url: + file: ./postgres_url + +volumes: + healthcare_data: diff --git a/compose-agents/healthcare-insurance/postgres_url b/compose-agents/healthcare-insurance/postgres_url new file mode 100644 index 00000000..ffcac169 --- /dev/null +++ b/compose-agents/healthcare-insurance/postgres_url @@ -0,0 +1 @@ +postgres://healthcare:secure_password@database:5432/claims \ No newline at end of file diff --git a/compose-agents/sql-analyst/Dockerfile b/compose-agents/sql-analyst/Dockerfile new file mode 100644 index 00000000..e38e9c1a --- /dev/null +++ b/compose-agents/sql-analyst/Dockerfile @@ -0,0 +1,17 @@ +# BrainSait SQL Analyst +FROM python:3.12-slim + +WORKDIR /app + +RUN pip install --no-cache-dir \ + langgraph \ + langchain-core \ + httpx \ + uvicorn \ + psycopg2-binary + +COPY agent.py ./ + +ENV PYTHONUNBUFFERED=1 + +CMD ["python", "agent.py"] diff --git a/compose-agents/sql-analyst/agent.py b/compose-agents/sql-analyst/agent.py new file mode 100644 index 00000000..13c09307 --- /dev/null +++ b/compose-agents/sql-analyst/agent.py @@ -0,0 +1,248 @@ +"""BrainSait SQL Analyst - LangGraph Agent for Natural Language to SQL""" + +import os +import json +from typing import TypedDict, Annotated, Sequence +from langgraph.graph import StateGraph, END +from langchain_core.messages import HumanMessage, AIMessage, BaseMessage +import httpx +import uvicorn +from fastapi import FastAPI, Request +from fastapi.responses import HTMLResponse, JSONResponse +from fastapi.staticfiles import StaticFiles + +# State definition +class AgentState(TypedDict): + messages: Annotated[Sequence[BaseMessage], lambda x, y: x + y] + question: str + sql_query: str + query_result: str + final_answer: str + + +# LLM client +class LLMClient: + def __init__(self): + self.base_url = os.environ.get("MODEL_RUNNER_URL", "http://localhost:8080/v1") + self.model = os.environ.get("MODEL_RUNNER_MODEL", "ai/qwen3:14B-Q6_K") + + async def chat(self, messages: list[dict], system_prompt: str = None) -> str: + async with httpx.AsyncClient() as client: + payload = { + "model": self.model, + "messages": messages, + "temperature": 0.1, + } + if system_prompt: + payload["messages"] = [{"role": "system", "content": system_prompt}] + payload["messages"] + + response = await client.post( + f"{self.base_url}/chat/completions", + json=payload, + timeout=120.0 + ) + response.raise_for_status() + return response.json()["choices"][0]["message"]["content"] + + +# MCP client for database queries +class MCPClient: + def __init__(self): + self.mcp_url = os.environ.get("MCP_SERVER_URL", "http://mcp-gateway:8811/sse") + + async def query(self, sql: str) -> str: + async with httpx.AsyncClient() as client: + response = await client.post( + self.mcp_url.replace("/sse", "/tools/query"), + json={"sql": sql}, + timeout=60.0 + ) + if response.status_code == 200: + return json.dumps(response.json(), indent=2) + return f"Error: {response.text}" + + +llm = LLMClient() +mcp = MCPClient() + + +# Agent nodes +async def understand_question(state: AgentState) -> dict: + """Understand the user's question and identify required data.""" + system_prompt = """You are a SQL expert. Analyze the user's question and identify: +1. What data they're looking for +2. What tables might be relevant +3. Any filters or aggregations needed + +Respond with a brief analysis.""" + + messages = [{"role": "user", "content": state["question"]}] + response = await llm.chat(messages, system_prompt) + + return { + "messages": [AIMessage(content=f"Analysis: {response}")] + } + + +async def generate_sql(state: AgentState) -> dict: + """Generate SQL query based on the question.""" + system_prompt = """You are a SQL expert for PostgreSQL. Generate a SQL query to answer the user's question. + +Rules: +- Use only SELECT statements (no INSERT, UPDATE, DELETE) +- Include appropriate JOINs if needed +- Add LIMIT 100 to prevent large result sets +- Format the query nicely + +Respond with ONLY the SQL query, no explanation.""" + + messages = [{"role": "user", "content": state["question"]}] + sql_query = await llm.chat(messages, system_prompt) + + # Clean up the query + sql_query = sql_query.strip() + if sql_query.startswith("```"): + sql_query = sql_query.split("```")[1] + if sql_query.startswith("sql"): + sql_query = sql_query[3:] + sql_query = sql_query.strip() + + return { + "sql_query": sql_query, + "messages": [AIMessage(content=f"Generated SQL:\n```sql\n{sql_query}\n```")] + } + + +async def execute_query(state: AgentState) -> dict: + """Execute the SQL query against the database.""" + result = await mcp.query(state["sql_query"]) + + return { + "query_result": result, + "messages": [AIMessage(content=f"Query Result:\n{result}")] + } + + +async def generate_answer(state: AgentState) -> dict: + """Generate a natural language answer from the query results.""" + system_prompt = """You are a data analyst. Given the user's question and the query results, +provide a clear, natural language answer. Include relevant numbers and insights.""" + + messages = [ + {"role": "user", "content": f"Question: {state['question']}\n\nQuery Results:\n{state['query_result']}"} + ] + answer = await llm.chat(messages, system_prompt) + + return { + "final_answer": answer, + "messages": [AIMessage(content=answer)] + } + + +# Build the graph +def build_graph(): + workflow = StateGraph(AgentState) + + workflow.add_node("understand", understand_question) + workflow.add_node("generate_sql", generate_sql) + workflow.add_node("execute", execute_query) + workflow.add_node("answer", generate_answer) + + workflow.set_entry_point("understand") + workflow.add_edge("understand", "generate_sql") + workflow.add_edge("generate_sql", "execute") + workflow.add_edge("execute", "answer") + workflow.add_edge("answer", END) + + return workflow.compile() + + +# FastAPI app +app = FastAPI(title="BrainSait SQL Analyst") +graph = build_graph() + + +@app.get("/", response_class=HTMLResponse) +async def index(): + return """ + + + + BrainSait SQL Analyst + + + +

🔍 BrainSait SQL Analyst

+

Ask questions about your data in natural language.

+ +
+ + +
+ +
+ + + + + """ + + +@app.post("/query") +async def query(request: Request): + data = await request.json() + question = data.get("question", "") + + initial_state = { + "messages": [HumanMessage(content=question)], + "question": question, + "sql_query": "", + "query_result": "", + "final_answer": "" + } + + result = await graph.ainvoke(initial_state) + + return JSONResponse({ + "question": question, + "sql_query": result["sql_query"], + "result": result["query_result"], + "answer": result["final_answer"] + }) + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/compose-agents/sql-analyst/compose.github.yaml b/compose-agents/sql-analyst/compose.github.yaml new file mode 100644 index 00000000..f9b4b0b3 --- /dev/null +++ b/compose-agents/sql-analyst/compose.github.yaml @@ -0,0 +1,9 @@ +# GitHub Models Configuration +# Use with: docker compose -f compose.yaml -f compose.github.yaml up + +services: + agent: + environment: + - MODEL_RUNNER_URL=https://models.inference.ai.azure.com + - MODEL_RUNNER_MODEL=openai/gpt-4o-mini + - GITHUB_TOKEN=${GITHUB_TOKEN} diff --git a/compose-agents/sql-analyst/compose.yaml b/compose-agents/sql-analyst/compose.yaml new file mode 100644 index 00000000..5fd17c2f --- /dev/null +++ b/compose-agents/sql-analyst/compose.yaml @@ -0,0 +1,58 @@ +# BrainSait SQL Analyst +# LangGraph-based natural language to SQL agent + +services: + database: + image: postgres:16-alpine + environment: + POSTGRES_USER: analyst + POSTGRES_PASSWORD: analyst_password + POSTGRES_DB: analytics + volumes: + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + - analytics_data:/var/lib/postgresql/data + healthcheck: + test: [CMD-SHELL, pg_isready -U analyst -d analytics] + interval: 5s + timeout: 3s + retries: 10 + + agent: + build: . + ports: + - 8080:8080 + environment: + - MCP_SERVER_URL=http://mcp-gateway:8811/sse + - DATABASE_DIALECT=PostgreSQL + depends_on: + database: + condition: service_healthy + mcp-gateway: + condition: service_started + models: + qwen3: + endpoint_var: MODEL_RUNNER_URL + model_var: MODEL_RUNNER_MODEL + + mcp-gateway: + image: docker/mcp-gateway:latest + use_api_socket: true + command: + - --transport=sse + - --secrets=/run/secrets/database-url + - --servers=postgres + - --tools=query + secrets: + - database-url + +models: + qwen3: + model: ai/qwen3:14B-Q6_K + context_size: 16384 + +secrets: + database-url: + file: ./postgres_url + +volumes: + analytics_data: diff --git a/compose-agents/sql-analyst/init.sql b/compose-agents/sql-analyst/init.sql new file mode 100644 index 00000000..c125b3ee --- /dev/null +++ b/compose-agents/sql-analyst/init.sql @@ -0,0 +1,148 @@ +-- BrainSait SQL Analyst - Sample Analytics Database + +-- Products table +CREATE TABLE products ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + category VARCHAR(100), + price DECIMAL(10,2), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Customers table +CREATE TABLE customers ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) UNIQUE, + country VARCHAR(100), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Orders table +CREATE TABLE orders ( + id SERIAL PRIMARY KEY, + customer_id INTEGER REFERENCES customers(id), + total_amount DECIMAL(10,2), + status VARCHAR(50) DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Order items table +CREATE TABLE order_items ( + id SERIAL PRIMARY KEY, + order_id INTEGER REFERENCES orders(id), + product_id INTEGER REFERENCES products(id), + quantity INTEGER, + unit_price DECIMAL(10,2) +); + +-- Sample data: Products +INSERT INTO products (name, category, price) VALUES + ('Laptop Pro 15', 'Electronics', 1299.99), + ('Wireless Mouse', 'Electronics', 49.99), + ('USB-C Hub', 'Electronics', 79.99), + ('Standing Desk', 'Furniture', 599.99), + ('Ergonomic Chair', 'Furniture', 449.99), + ('Monitor 27"', 'Electronics', 399.99), + ('Keyboard Mechanical', 'Electronics', 149.99), + ('Desk Lamp', 'Furniture', 89.99), + ('Webcam HD', 'Electronics', 129.99), + ('Headphones', 'Electronics', 199.99); + +-- Sample data: Customers +INSERT INTO customers (name, email, country) VALUES + ('John Smith', 'john@example.com', 'USA'), + ('Maria Garcia', 'maria@example.com', 'Spain'), + ('Ahmed Hassan', 'ahmed@example.com', 'Egypt'), + ('Yuki Tanaka', 'yuki@example.com', 'Japan'), + ('Emma Wilson', 'emma@example.com', 'UK'), + ('Mohammed Ali', 'mo@example.com', 'UAE'), + ('Lisa Chen', 'lisa@example.com', 'China'), + ('Hans Mueller', 'hans@example.com', 'Germany'), + ('Sarah Johnson', 'sarah@example.com', 'USA'), + ('Carlos Rodriguez', 'carlos@example.com', 'Mexico'); + +-- Sample data: Orders (last 3 months) +INSERT INTO orders (customer_id, total_amount, status, created_at) VALUES + (1, 1349.98, 'completed', NOW() - INTERVAL '5 days'), + (2, 599.99, 'completed', NOW() - INTERVAL '10 days'), + (3, 249.98, 'completed', NOW() - INTERVAL '15 days'), + (4, 1749.97, 'completed', NOW() - INTERVAL '20 days'), + (5, 449.99, 'completed', NOW() - INTERVAL '25 days'), + (6, 879.98, 'completed', NOW() - INTERVAL '30 days'), + (7, 1299.99, 'completed', NOW() - INTERVAL '35 days'), + (8, 329.98, 'shipped', NOW() - INTERVAL '3 days'), + (9, 649.98, 'shipped', NOW() - INTERVAL '2 days'), + (10, 199.99, 'pending', NOW() - INTERVAL '1 day'), + (1, 449.99, 'completed', NOW() - INTERVAL '45 days'), + (3, 1299.99, 'completed', NOW() - INTERVAL '50 days'), + (5, 279.98, 'completed', NOW() - INTERVAL '55 days'), + (7, 599.99, 'completed', NOW() - INTERVAL '60 days'), + (9, 849.98, 'completed', NOW() - INTERVAL '65 days'); + +-- Sample data: Order items +INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES + (1, 1, 1, 1299.99), + (1, 2, 1, 49.99), + (2, 4, 1, 599.99), + (3, 2, 2, 49.99), + (3, 7, 1, 149.99), + (4, 1, 1, 1299.99), + (4, 5, 1, 449.99), + (5, 5, 1, 449.99), + (6, 6, 1, 399.99), + (6, 3, 1, 79.99), + (6, 10, 2, 199.99), + (7, 1, 1, 1299.99), + (8, 7, 1, 149.99), + (8, 9, 1, 129.99), + (8, 2, 1, 49.99), + (9, 4, 1, 599.99), + (9, 2, 1, 49.99), + (10, 10, 1, 199.99), + (11, 5, 1, 449.99), + (12, 1, 1, 1299.99), + (13, 3, 2, 79.99), + (13, 8, 1, 89.99), + (13, 2, 1, 49.99), + (14, 4, 1, 599.99), + (15, 6, 1, 399.99), + (15, 5, 1, 449.99); + +-- Create useful views +CREATE VIEW monthly_sales AS +SELECT + DATE_TRUNC('month', o.created_at) as month, + COUNT(DISTINCT o.id) as order_count, + SUM(o.total_amount) as total_sales, + COUNT(DISTINCT o.customer_id) as unique_customers +FROM orders o +WHERE o.status IN ('completed', 'shipped') +GROUP BY DATE_TRUNC('month', o.created_at) +ORDER BY month DESC; + +CREATE VIEW product_performance AS +SELECT + p.id, + p.name, + p.category, + COUNT(oi.id) as times_ordered, + SUM(oi.quantity) as total_quantity, + SUM(oi.quantity * oi.unit_price) as total_revenue +FROM products p +LEFT JOIN order_items oi ON p.id = oi.product_id +GROUP BY p.id, p.name, p.category +ORDER BY total_revenue DESC; + +CREATE VIEW customer_segments AS +SELECT + c.id, + c.name, + c.country, + COUNT(o.id) as order_count, + SUM(o.total_amount) as total_spent, + MAX(o.created_at) as last_order +FROM customers c +LEFT JOIN orders o ON c.id = o.customer_id +GROUP BY c.id, c.name, c.country +ORDER BY total_spent DESC; diff --git a/compose-agents/sql-analyst/postgres_url b/compose-agents/sql-analyst/postgres_url new file mode 100644 index 00000000..34786ad4 --- /dev/null +++ b/compose-agents/sql-analyst/postgres_url @@ -0,0 +1 @@ +postgres://analyst:analyst_password@database:5432/analytics \ No newline at end of file diff --git a/compose-agents/start.sh b/compose-agents/start.sh new file mode 100644 index 00000000..357a2ff0 --- /dev/null +++ b/compose-agents/start.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# BrainSait Compose Agents - Quick Start Script + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +print_header() { + echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" + echo -e "${BLUE}║${NC} ${GREEN}BrainSait Multi-Agent AI Platform${NC} ${BLUE}║${NC}" + echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" + echo "" +} + +print_menu() { + echo -e "${YELLOW}Available Agent Systems:${NC}" + echo "" + echo " 1) 🌍 Arabic Assistant - Translation, writing, research in Arabic" + echo " 2) 🏥 Healthcare Insurance - Claims analysis, PA, coding, appeals" + echo " 3) 💻 Developer Assistant - Code review, architecture, debugging" + echo " 4) 🔍 SQL Analyst - Natural language to SQL queries" + echo "" + echo " q) Quit" + echo "" +} + +check_docker() { + if ! command -v docker &> /dev/null; then + echo -e "${RED}Error: Docker is not installed.${NC}" + echo "Please install Docker Desktop: https://www.docker.com/products/docker-desktop/" + exit 1 + fi + + if ! docker info &> /dev/null; then + echo -e "${RED}Error: Docker daemon is not running.${NC}" + echo "Please start Docker Desktop." + exit 1 + fi +} + +check_github_token() { + if [ -z "$GITHUB_TOKEN" ]; then + echo -e "${YELLOW}GitHub token not set.${NC}" + + # Try to get from gh CLI + if command -v gh &> /dev/null; then + export GITHUB_TOKEN=$(gh auth token 2>/dev/null) + if [ -n "$GITHUB_TOKEN" ]; then + echo -e "${GREEN}✓ Using GitHub token from gh CLI${NC}" + return 0 + fi + fi + + echo "" + echo "To use GitHub Models, set your token:" + echo " export GITHUB_TOKEN=\$(gh auth token)" + echo " # or" + echo " export GITHUB_TOKEN=ghp_xxxxx" + echo "" + return 1 + fi + echo -e "${GREEN}✓ GitHub token configured${NC}" + return 0 +} + +run_agent() { + local agent_dir=$1 + local agent_name=$2 + local use_github=${3:-false} + + echo "" + echo -e "${GREEN}Starting $agent_name...${NC}" + echo "" + + cd "$SCRIPT_DIR/$agent_dir" + + if [ "$use_github" = true ] && [ -f "compose.github.yaml" ]; then + echo "Using GitHub Models..." + docker compose -f compose.yaml -f compose.github.yaml up --build + else + echo "Using Docker Model Runner (local)..." + docker compose up --build + fi +} + +main() { + print_header + check_docker + + local has_token=false + if check_github_token; then + has_token=true + fi + + echo "" + + while true; do + print_menu + read -p "Select an agent (1-4, or q to quit): " choice + + case $choice in + 1) + run_agent "arabic-assistant" "Arabic Language Assistant" $has_token + ;; + 2) + run_agent "healthcare-insurance" "Healthcare Insurance Analyst" $has_token + ;; + 3) + run_agent "developer-assistant" "Developer Assistant" $has_token + ;; + 4) + run_agent "sql-analyst" "SQL Analyst" $has_token + ;; + q|Q) + echo "" + echo -e "${GREEN}Goodbye!${NC}" + exit 0 + ;; + *) + echo -e "${RED}Invalid selection. Please try again.${NC}" + echo "" + ;; + esac + done +} + +main "$@" diff --git a/db/init.sql b/db/init.sql new file mode 100644 index 00000000..76a23a79 --- /dev/null +++ b/db/init.sql @@ -0,0 +1,221 @@ +-- BrainSait AI Platform - Database Schema +-- PostgreSQL 15+ + +-- Enable extensions +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; + +-- ============================================ +-- Users & Authentication +-- ============================================ +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + email VARCHAR(255) UNIQUE NOT NULL, + name VARCHAR(255), + api_key VARCHAR(64) UNIQUE NOT NULL DEFAULT encode(gen_random_bytes(32), 'hex'), + tier VARCHAR(50) DEFAULT 'free' CHECK (tier IN ('free', 'pro', 'enterprise')), + organization VARCHAR(255), + domain VARCHAR(50) DEFAULT 'general' CHECK (domain IN ('general', 'arabic', 'healthcare', 'developer')), + language VARCHAR(10) DEFAULT 'en', + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Index for API key lookups (critical for performance) +CREATE INDEX idx_users_api_key ON users(api_key) WHERE is_active = true; +CREATE INDEX idx_users_email ON users(email); + +-- ============================================ +-- Usage Tracking +-- ============================================ +CREATE TABLE usage_logs ( + id BIGSERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + endpoint VARCHAR(100) NOT NULL, + method VARCHAR(10) DEFAULT 'POST', + model_used VARCHAR(100), + domain VARCHAR(50), + tokens_input INT DEFAULT 0, + tokens_output INT DEFAULT 0, + credits_used INT DEFAULT 1, + latency_ms INT, + status_code INT, + error_message TEXT, + metadata JSONB DEFAULT '{}', + ip_address INET, + user_agent TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Partitioning by month for better performance +CREATE INDEX idx_usage_user_time ON usage_logs(user_id, created_at DESC); +CREATE INDEX idx_usage_domain ON usage_logs(domain, created_at DESC); + +-- ============================================ +-- Billing & Subscriptions +-- ============================================ +CREATE TABLE subscriptions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID UNIQUE REFERENCES users(id) ON DELETE CASCADE, + stripe_customer_id VARCHAR(100), + stripe_subscription_id VARCHAR(100), + plan VARCHAR(50) NOT NULL DEFAULT 'free', + status VARCHAR(50) DEFAULT 'active' CHECK (status IN ('active', 'canceled', 'past_due', 'trialing')), + monthly_credits INT DEFAULT 100, + credits_used INT DEFAULT 0, + current_period_start DATE, + current_period_end DATE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_subscriptions_stripe ON subscriptions(stripe_customer_id); + +-- ============================================ +-- Invoices +-- ============================================ +CREATE TABLE invoices ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + stripe_invoice_id VARCHAR(100), + amount_cents INT NOT NULL, + currency VARCHAR(3) DEFAULT 'USD', + status VARCHAR(50) DEFAULT 'pending', + period_start DATE, + period_end DATE, + paid_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- ============================================ +-- Prompts Library +-- ============================================ +CREATE TABLE prompts ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + user_id UUID REFERENCES users(id) ON DELETE SET NULL, + name VARCHAR(255) NOT NULL, + description TEXT, + domain VARCHAR(50) DEFAULT 'general', + language VARCHAR(10) DEFAULT 'en', + content JSONB NOT NULL, -- Store .prompt.yml as JSON + is_public BOOLEAN DEFAULT false, + is_featured BOOLEAN DEFAULT false, + usage_count INT DEFAULT 0, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_prompts_domain ON prompts(domain) WHERE is_public = true; +CREATE INDEX idx_prompts_language ON prompts(language); + +-- ============================================ +-- API Rate Limiting +-- ============================================ +CREATE TABLE rate_limits ( + id SERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id) ON DELETE CASCADE, + endpoint VARCHAR(100), + requests_count INT DEFAULT 0, + window_start TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + window_size_seconds INT DEFAULT 60, + max_requests INT DEFAULT 60 +); + +CREATE UNIQUE INDEX idx_rate_limits_user_endpoint ON rate_limits(user_id, endpoint); + +-- ============================================ +-- Audit Log (HIPAA Compliance) +-- ============================================ +CREATE TABLE audit_logs ( + id BIGSERIAL PRIMARY KEY, + user_id UUID REFERENCES users(id) ON DELETE SET NULL, + action VARCHAR(100) NOT NULL, + resource_type VARCHAR(100), + resource_id VARCHAR(255), + details JSONB DEFAULT '{}', + ip_address INET, + user_agent TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_audit_user ON audit_logs(user_id, created_at DESC); +CREATE INDEX idx_audit_action ON audit_logs(action, created_at DESC); + +-- ============================================ +-- Functions & Triggers +-- ============================================ + +-- Auto-update updated_at timestamp +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ language 'plpgsql'; + +CREATE TRIGGER update_users_updated_at + BEFORE UPDATE ON users + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE TRIGGER update_subscriptions_updated_at + BEFORE UPDATE ON subscriptions + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE TRIGGER update_prompts_updated_at + BEFORE UPDATE ON prompts + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +-- ============================================ +-- Views +-- ============================================ + +-- User usage summary +CREATE VIEW user_usage_summary AS +SELECT + u.id, + u.email, + u.tier, + u.domain, + s.monthly_credits, + s.credits_used, + (s.monthly_credits - s.credits_used) as credits_remaining, + COUNT(ul.id) as total_requests, + SUM(ul.tokens_input + ul.tokens_output) as total_tokens +FROM users u +LEFT JOIN subscriptions s ON u.id = s.user_id +LEFT JOIN usage_logs ul ON u.id = ul.user_id + AND ul.created_at >= s.current_period_start +GROUP BY u.id, u.email, u.tier, u.domain, s.monthly_credits, s.credits_used; + +-- Daily usage analytics +CREATE VIEW daily_usage_analytics AS +SELECT + DATE(created_at) as date, + domain, + model_used, + COUNT(*) as request_count, + SUM(tokens_input) as total_input_tokens, + SUM(tokens_output) as total_output_tokens, + AVG(latency_ms) as avg_latency_ms, + COUNT(CASE WHEN status_code >= 400 THEN 1 END) as error_count +FROM usage_logs +WHERE created_at >= NOW() - INTERVAL '30 days' +GROUP BY DATE(created_at), domain, model_used +ORDER BY date DESC; + +-- ============================================ +-- Seed Data: Default Tiers +-- ============================================ +INSERT INTO users (email, name, tier, domain, api_key) VALUES + ('system@brainsait.ai', 'System', 'enterprise', 'general', 'system_internal_key_do_not_use'), + ('demo@brainsait.ai', 'Demo User', 'pro', 'developer', 'demo_api_key_for_testing_only'); + +-- Add default subscription for demo user +INSERT INTO subscriptions (user_id, plan, monthly_credits, current_period_start, current_period_end) +SELECT id, 'pro', 10000, DATE_TRUNC('month', NOW()), DATE_TRUNC('month', NOW()) + INTERVAL '1 month' +FROM users WHERE email = 'demo@brainsait.ai'; diff --git a/deploy/.env.production.template b/deploy/.env.production.template new file mode 100644 index 00000000..6027860c --- /dev/null +++ b/deploy/.env.production.template @@ -0,0 +1,91 @@ +# BrainSait Production Environment Configuration +# Copy to .env and fill in your values + +# =========================================== +# Core Configuration +# =========================================== +BRAINSAIT_ENV=production +BRAINSAIT_PORT=8080 +BRAINSAIT_DEBUG=false + +# =========================================== +# Docker Registry +# =========================================== +DOCKER_REGISTRY=docker.io +DOCKER_USERNAME=brainsait +VERSION=latest + +# =========================================== +# Authentication +# =========================================== +# GitHub Token for Models API +GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx + +# API Key Encryption Secret (generate with: openssl rand -hex 32) +API_KEY_SECRET=your-api-key-secret-here + +# =========================================== +# Database +# =========================================== +DB_PASSWORD=your-secure-database-password +DATABASE_URL=postgres://brainsait:${DB_PASSWORD}@postgres:5432/brainsait?sslmode=disable + +# =========================================== +# Redis +# =========================================== +REDIS_URL=redis://redis:6379 + +# =========================================== +# Stripe Billing +# =========================================== +STRIPE_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxx +STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxx +STRIPE_PUBLISHABLE_KEY=pk_live_xxxxxxxxxxxxxxxxxxxx + +# Stripe Price IDs (create in Stripe Dashboard) +STRIPE_PRICE_DEVELOPER=price_xxxxxxxxxxxx +STRIPE_PRICE_TEAM=price_xxxxxxxxxxxx +STRIPE_PRICE_ENTERPRISE=price_xxxxxxxxxxxx + +# =========================================== +# Cloudflare Tunnel +# =========================================== +CF_TUNNEL_TOKEN=your-cloudflare-tunnel-token + +# =========================================== +# Domains (via Cloudflare) +# =========================================== +DOMAIN_API=api.brainsait.ai +DOMAIN_DOCS=docs.brainsait.ai +DOMAIN_APP=app.brainsait.ai + +# =========================================== +# CORS +# =========================================== +CORS_ORIGINS=https://brainsait.ai,https://app.brainsait.ai,https://docs.brainsait.ai + +# =========================================== +# Monitoring +# =========================================== +GRAFANA_PASSWORD=your-grafana-admin-password + +# =========================================== +# Email (for notifications) +# =========================================== +SMTP_HOST=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_USER=apikey +SMTP_PASSWORD=your-sendgrid-api-key +SMTP_FROM=noreply@brainsait.ai + +# =========================================== +# Rate Limiting +# =========================================== +RATE_LIMIT_REQUESTS=100 +RATE_LIMIT_WINDOW_SECONDS=60 + +# =========================================== +# Logging +# =========================================== +LOG_LEVEL=info +LOG_FORMAT=json diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 00000000..288e64bd --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,301 @@ +# BrainSait Deployment Guide + +This guide covers the complete deployment workflow for the BrainSait AI Platform. + +## Overview + +The deployment process follows these steps: + +1. **Build** - Create Docker images locally +2. **Push** - Upload images to Docker Hub +3. **Deploy** - Pull and run images on a remote VM +4. **Expose** - Use Cloudflare Tunnel to expose services + +## Prerequisites + +- Docker and Docker Compose installed locally +- Docker Hub account +- Remote VM (Ubuntu 22.04 LTS recommended) +- Cloudflare account with a domain +- GitHub Personal Access Token (for GitHub Models API) +- Stripe account (for billing) + +## Quick Start + +### 1. Build Images Locally + +```bash +# Build the main CLI image +docker build -t brainsait/brainsait-ai:latest . + +# Build the API server image +docker build -f Dockerfile.api -t brainsait/brainsait-ai-api:latest . +``` + +### 2. Push to Docker Hub + +```bash +# Login to Docker Hub +docker login + +# Push images +./deploy/docker-hub-push.sh +``` + +Or manually: + +```bash +docker push brainsait/brainsait-ai:latest +docker push brainsait/brainsait-ai-api:latest +``` + +### 3. Setup Remote VM + +SSH into your VM and run: + +```bash +# Download and run setup script +curl -fsSL https://raw.githubusercontent.com/your-org/brainsait/main/deploy/vm-setup.sh | bash +``` + +Or manually: + +```bash +# Install Docker +curl -fsSL https://get.docker.com | sh +sudo usermod -aG docker $USER + +# Install Docker Compose +sudo apt install -y docker-compose-plugin + +# Install Cloudflared +curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb +sudo dpkg -i cloudflared.deb +``` + +### 4. Configure Environment + +```bash +cd /opt/brainsait +cp .env.template .env +nano .env +``` + +Required environment variables: + +| Variable | Description | +|----------|-------------| +| `GITHUB_TOKEN` | GitHub PAT with models access | +| `STRIPE_API_KEY` | Stripe secret key | +| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signing secret | +| `DB_PASSWORD` | PostgreSQL password | +| `GRAFANA_PASSWORD` | Grafana admin password | + +### 5. Start Services + +```bash +docker-compose up -d + +# Check status +docker-compose ps +docker-compose logs -f api +``` + +### 6. Setup Cloudflare Tunnel + +```bash +# Login to Cloudflare +cloudflared tunnel login + +# Create tunnel +cloudflared tunnel create brainsait-tunnel + +# Configure DNS routes +cloudflared tunnel route dns brainsait-tunnel api.yourdomain.com +cloudflared tunnel route dns brainsait-tunnel docs.yourdomain.com + +# Install as service +sudo cloudflared service install + +# Start tunnel +sudo systemctl start cloudflared +sudo systemctl enable cloudflared +``` + +## Architecture + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Cloudflare Edge │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ +│ │ api.domain │ │docs.domain │ │ grafana.domain │ │ +│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │ +└─────────┼────────────────┼────────────────────┼──────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Cloudflare Tunnel │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ Remote VM │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ Docker Network │ │ +│ │ ┌─────────┐ ┌──────────┐ ┌───────┐ ┌─────────┐ │ │ +│ │ │ API │──│ Postgres │ │ Redis │ │Prometheus│ │ │ +│ │ │ :8080 │ │ :5432 │ │ :6379 │ │ :9090 │ │ │ +│ │ └─────────┘ └──────────┘ └───────┘ └─────────┘ │ │ +│ │ ┌─────────┐ │ │ +│ │ │ Grafana │ │ │ +│ │ │ :3001 │ │ │ +│ │ └─────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +## API Endpoints + +Once deployed, the following endpoints are available: + +### Public Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/health` | GET | Health check | +| `/v1/chat` | POST | Chat completion | +| `/v1/chat/stream` | POST | Streaming chat | +| `/v1/models` | GET | List available models | +| `/v1/models/{id}` | GET | Get model details | +| `/v1/prompts` | GET | List prompt templates | +| `/v1/prompts/{domain}` | GET | Domain-specific prompts | + +### Authenticated Endpoints + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/v1/user` | GET | Get user profile | +| `/v1/user/usage` | GET | Get usage statistics | +| `/v1/billing/checkout` | POST | Create Stripe checkout | +| `/v1/billing/portal` | POST | Create billing portal | + +## Monitoring + +### Grafana Dashboards + +Access Grafana at `https://grafana.yourdomain.com` with: +- Username: `admin` +- Password: (from `GRAFANA_PASSWORD` in .env) + +### Prometheus Metrics + +The API server exposes metrics at `/metrics`: +- Request counts and latency +- Token usage +- Error rates + +## Backup & Recovery + +### Automated Backups + +The VM setup configures daily PostgreSQL backups at 2 AM: + +```bash +# Manual backup +/opt/brainsait/backup.sh + +# View backups +ls -la /opt/brainsait/backups/ +``` + +### Restore from Backup + +```bash +# Stop services +docker-compose down + +# Restore database +gunzip < backups/db_YYYYMMDD_HHMMSS.sql.gz | \ + docker exec -i brainsait-postgres psql -U brainsait brainsait + +# Start services +docker-compose up -d +``` + +## Troubleshooting + +### Check Service Status + +```bash +# Container status +docker-compose ps + +# Container logs +docker-compose logs -f api +docker-compose logs -f postgres + +# Tunnel status +sudo systemctl status cloudflared +``` + +### Common Issues + +**API not responding:** +```bash +# Check health +curl http://localhost:8080/health + +# Check logs +docker-compose logs api +``` + +**Database connection failed:** +```bash +# Check PostgreSQL +docker-compose logs postgres +docker exec -it brainsait-postgres psql -U brainsait -d brainsait +``` + +**Tunnel not working:** +```bash +# Check tunnel status +cloudflared tunnel info brainsait-tunnel + +# Check tunnel logs +sudo journalctl -u cloudflared -f +``` + +## Scaling + +### Horizontal Scaling + +To run multiple API instances: + +```yaml +# In docker-compose.yml +services: + api: + deploy: + replicas: 3 +``` + +### Load Balancing + +Cloudflare automatically load balances across tunnel instances. + +## Security Checklist + +- [ ] Use strong passwords in `.env` +- [ ] Enable Cloudflare Access for admin endpoints +- [ ] Configure firewall (only allow ports 22, 80, 443) +- [ ] Enable automatic security updates +- [ ] Set up SSL certificate rotation +- [ ] Enable audit logging +- [ ] Configure rate limiting in Cloudflare + +## Support + +For issues or questions: +- GitHub Issues: https://github.com/your-org/brainsait/issues +- Documentation: https://docs.brainsait.com +- Email: support@brainsait.com diff --git a/deploy/cloudflare/config.yml b/deploy/cloudflare/config.yml new file mode 100644 index 00000000..f8e674ac --- /dev/null +++ b/deploy/cloudflare/config.yml @@ -0,0 +1,38 @@ +# Cloudflare Tunnel Configuration for BrainSait +# This file configures how traffic routes through the tunnel + +tunnel: brainsait-tunnel +credentials-file: /etc/cloudflared/credentials.json + +# Route traffic to local services +ingress: + # API Gateway - main endpoint + - hostname: api.brainsait.com + service: http://localhost:8080 + originRequest: + connectTimeout: 30s + noTLSVerify: false + + # Documentation site + - hostname: docs.brainsait.com + service: http://localhost:8081 + + # Monitoring dashboards (internal access only) + - hostname: metrics.brainsait.com + service: http://localhost:3000 + originRequest: + # Require Cloudflare Access authentication + access: + required: true + teamName: brainsait + + # Grafana dashboards + - hostname: grafana.brainsait.com + service: http://localhost:3001 + originRequest: + access: + required: true + teamName: brainsait + + # Catch-all rule (required) + - service: http_status:404 diff --git a/deploy/cloudflare/tunnel-setup.sh b/deploy/cloudflare/tunnel-setup.sh new file mode 100644 index 00000000..b5710bc1 --- /dev/null +++ b/deploy/cloudflare/tunnel-setup.sh @@ -0,0 +1,114 @@ +#!/bin/bash +# Cloudflare Tunnel Setup Script for BrainSait +# Prerequisites: cloudflared CLI installed and authenticated + +set -e + +TUNNEL_NAME="${TUNNEL_NAME:-brainsait-tunnel}" +DOMAIN="${DOMAIN:-brainsait.com}" + +echo "🌐 BrainSait Cloudflare Tunnel Setup" +echo "======================================" + +# Check if cloudflared is installed +if ! command -v cloudflared &> /dev/null; then + echo "❌ cloudflared CLI not found. Installing..." + + # Detect OS and install + if [[ "$OSTYPE" == "darwin"* ]]; then + brew install cloudflare/cloudflare/cloudflared + elif [[ -f /etc/debian_version ]]; then + curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb + sudo dpkg -i cloudflared.deb + rm cloudflared.deb + elif [[ -f /etc/redhat-release ]]; then + curl -L --output cloudflared.rpm https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-x86_64.rpm + sudo rpm -i cloudflared.rpm + rm cloudflared.rpm + else + echo "❌ Unsupported OS. Please install cloudflared manually." + exit 1 + fi +fi + +echo "✅ cloudflared installed: $(cloudflared --version)" + +# Login to Cloudflare (if not already) +echo "" +echo "📝 Step 1: Authenticate with Cloudflare" +if [ ! -f ~/.cloudflared/cert.pem ]; then + echo "Opening browser for authentication..." + cloudflared tunnel login +else + echo "Already authenticated" +fi + +# Create tunnel +echo "" +echo "📝 Step 2: Create tunnel '$TUNNEL_NAME'" +if cloudflared tunnel list | grep -q "$TUNNEL_NAME"; then + echo "Tunnel '$TUNNEL_NAME' already exists" + TUNNEL_ID=$(cloudflared tunnel list | grep "$TUNNEL_NAME" | awk '{print $1}') +else + cloudflared tunnel create "$TUNNEL_NAME" + TUNNEL_ID=$(cloudflared tunnel list | grep "$TUNNEL_NAME" | awk '{print $1}') + echo "Created tunnel with ID: $TUNNEL_ID" +fi + +# Configure DNS routes +echo "" +echo "📝 Step 3: Configure DNS routes" + +configure_dns() { + local subdomain=$1 + local hostname="${subdomain}.${DOMAIN}" + + if cloudflared tunnel route dns "$TUNNEL_NAME" "$hostname" 2>/dev/null; then + echo "✅ Configured: $hostname" + else + echo "⚠️ Route may already exist for: $hostname" + fi +} + +configure_dns "api" +configure_dns "docs" +configure_dns "metrics" +configure_dns "grafana" + +# Create credentials directory +echo "" +echo "📝 Step 4: Setup credentials" +sudo mkdir -p /etc/cloudflared +sudo cp ~/.cloudflared/${TUNNEL_ID}.json /etc/cloudflared/credentials.json +sudo chmod 600 /etc/cloudflared/credentials.json + +# Copy configuration +echo "" +echo "📝 Step 5: Install configuration" +sudo cp "$(dirname "$0")/config.yml" /etc/cloudflared/config.yml +sudo sed -i "s/brainsait-tunnel/$TUNNEL_ID/g" /etc/cloudflared/config.yml 2>/dev/null || \ +sudo sed -i '' "s/brainsait-tunnel/$TUNNEL_ID/g" /etc/cloudflared/config.yml + +# Install as service +echo "" +echo "📝 Step 6: Install as system service" +sudo cloudflared service install + +echo "" +echo "======================================" +echo "✅ Cloudflare Tunnel Setup Complete!" +echo "" +echo "Tunnel ID: $TUNNEL_ID" +echo "Endpoints configured:" +echo " - https://api.${DOMAIN} -> localhost:8080 (API)" +echo " - https://docs.${DOMAIN} -> localhost:8081 (Docs)" +echo " - https://metrics.${DOMAIN} -> localhost:3000 (Metrics)" +echo " - https://grafana.${DOMAIN} -> localhost:3001 (Grafana)" +echo "" +echo "Commands:" +echo " Start: sudo systemctl start cloudflared" +echo " Stop: sudo systemctl stop cloudflared" +echo " Status: sudo systemctl status cloudflared" +echo " Logs: sudo journalctl -u cloudflared -f" +echo "" +echo "Manual run: cloudflared tunnel run $TUNNEL_NAME" diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 00000000..7f483eb3 --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,247 @@ +#!/bin/bash +# BrainSait Deployment Script +# Builds, pushes to Docker Hub, and deploys to remote VM with Cloudflare Tunnel + +set -e + +# Configuration +DOCKER_REGISTRY="${DOCKER_REGISTRY:-docker.io}" +DOCKER_USERNAME="${DOCKER_USERNAME:-brainsait}" +IMAGE_NAME="${IMAGE_NAME:-brainsait-ai}" +VERSION="${VERSION:-latest}" +REMOTE_HOST="${REMOTE_HOST:-}" +REMOTE_USER="${REMOTE_USER:-root}" +CF_TUNNEL_TOKEN="${CF_TUNNEL_TOKEN:-}" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check required environment variables +check_requirements() { + log_info "Checking requirements..." + + if [ -z "$DOCKER_USERNAME" ]; then + log_error "DOCKER_USERNAME is required" + exit 1 + fi + + if [ -z "$REMOTE_HOST" ]; then + log_error "REMOTE_HOST is required for deployment" + exit 1 + fi + + # Check if Docker is installed + if ! command -v docker &> /dev/null; then + log_error "Docker is not installed" + exit 1 + fi + + log_info "All requirements met" +} + +# Build Docker image +build_image() { + log_info "Building Docker image..." + + docker build \ + --platform linux/amd64 \ + -t "$DOCKER_REGISTRY/$DOCKER_USERNAME/$IMAGE_NAME:$VERSION" \ + -t "$DOCKER_REGISTRY/$DOCKER_USERNAME/$IMAGE_NAME:latest" \ + -f Dockerfile \ + . + + log_info "Docker image built successfully" +} + +# Push to Docker Hub +push_image() { + log_info "Pushing image to Docker Hub..." + + # Login to Docker Hub + if [ -n "$DOCKER_PASSWORD" ]; then + echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin + else + log_warn "DOCKER_PASSWORD not set, assuming already logged in" + fi + + docker push "$DOCKER_REGISTRY/$DOCKER_USERNAME/$IMAGE_NAME:$VERSION" + docker push "$DOCKER_REGISTRY/$DOCKER_USERNAME/$IMAGE_NAME:latest" + + log_info "Image pushed successfully" +} + +# Deploy to remote VM +deploy_remote() { + log_info "Deploying to remote VM ($REMOTE_HOST)..." + + # Create deployment script + DEPLOY_SCRIPT=$(cat <<'EOF' +#!/bin/bash +set -e + +# Pull latest images +docker-compose pull + +# Stop existing containers +docker-compose down + +# Start new containers +docker-compose up -d + +# Clean up old images +docker image prune -f + +# Check health +sleep 10 +curl -f http://localhost:8080/health || exit 1 + +echo "Deployment successful!" +EOF +) + + # Copy docker-compose and .env files + scp docker-compose.yml "$REMOTE_USER@$REMOTE_HOST:~/brainsait/" + scp .env.production "$REMOTE_USER@$REMOTE_HOST:~/brainsait/.env" + + # Execute deployment + ssh "$REMOTE_USER@$REMOTE_HOST" "cd ~/brainsait && $DEPLOY_SCRIPT" + + log_info "Remote deployment completed" +} + +# Setup Cloudflare Tunnel +setup_cloudflare_tunnel() { + log_info "Setting up Cloudflare Tunnel..." + + if [ -z "$CF_TUNNEL_TOKEN" ]; then + log_warn "CF_TUNNEL_TOKEN not set, skipping tunnel setup" + return + fi + + # Create cloudflared config on remote + CF_CONFIG=$(cat < /etc/cloudflared/config.yml" + + # Install and start cloudflared + ssh "$REMOTE_USER@$REMOTE_HOST" << 'REMOTE_EOF' +# Install cloudflared if not present +if ! command -v cloudflared &> /dev/null; then + curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb + sudo dpkg -i cloudflared.deb + rm cloudflared.deb +fi + +# Setup tunnel as service +sudo cloudflared service install $CF_TUNNEL_TOKEN + +# Start tunnel +sudo systemctl start cloudflared +sudo systemctl enable cloudflared +REMOTE_EOF + + log_info "Cloudflare Tunnel configured" +} + +# Print usage +usage() { + cat << EOF +BrainSait Deployment Script + +Usage: $0 [command] + +Commands: + build Build Docker image only + push Push image to Docker Hub + deploy Deploy to remote VM + tunnel Setup Cloudflare Tunnel + all Run all steps (default) + +Environment Variables: + DOCKER_USERNAME Docker Hub username (required) + DOCKER_PASSWORD Docker Hub password (for push) + IMAGE_NAME Docker image name (default: brainsait-ai) + VERSION Image version tag (default: latest) + REMOTE_HOST Remote VM hostname/IP (required for deploy) + REMOTE_USER Remote VM user (default: root) + CF_TUNNEL_TOKEN Cloudflare Tunnel token + +Examples: + # Build and push + DOCKER_USERNAME=myuser VERSION=v1.0.0 $0 build push + + # Full deployment + DOCKER_USERNAME=myuser REMOTE_HOST=server.example.com $0 all +EOF +} + +# Main +main() { + if [ $# -eq 0 ]; then + set -- "all" + fi + + for cmd in "$@"; do + case "$cmd" in + build) + build_image + ;; + push) + push_image + ;; + deploy) + check_requirements + deploy_remote + ;; + tunnel) + setup_cloudflare_tunnel + ;; + all) + check_requirements + build_image + push_image + deploy_remote + setup_cloudflare_tunnel + ;; + -h|--help|help) + usage + exit 0 + ;; + *) + log_error "Unknown command: $cmd" + usage + exit 1 + ;; + esac + done + + log_info "All tasks completed successfully!" +} + +main "$@" diff --git a/deploy/docker-compose.production.yml b/deploy/docker-compose.production.yml new file mode 100644 index 00000000..f03404eb --- /dev/null +++ b/deploy/docker-compose.production.yml @@ -0,0 +1,165 @@ +# BrainSait Production Docker Compose +# For deployment on remote VM with Cloudflare Tunnel + +version: '3.8' + +services: + # Main API Server + api: + image: ${DOCKER_REGISTRY:-docker.io}/${DOCKER_USERNAME:-brainsait}/brainsait-ai:${VERSION:-latest} + container_name: brainsait-api + restart: unless-stopped + ports: + - "8080:8080" + environment: + - BRAINSAIT_ENV=production + - BRAINSAIT_PORT=8080 + - GITHUB_TOKEN=${GITHUB_TOKEN} + - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - DATABASE_URL=postgres://brainsait:${DB_PASSWORD}@postgres:5432/brainsait?sslmode=disable + - REDIS_URL=redis://redis:6379 + - CORS_ORIGINS=${CORS_ORIGINS:-https://brainsait.ai,https://app.brainsait.ai} + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s + networks: + - brainsait-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # PostgreSQL Database + postgres: + image: postgres:15-alpine + container_name: brainsait-postgres + restart: unless-stopped + environment: + - POSTGRES_USER=brainsait + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=brainsait + volumes: + - postgres-data:/var/lib/postgresql/data + - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U brainsait"] + interval: 10s + timeout: 5s + retries: 5 + networks: + - brainsait-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # Redis Cache + redis: + image: redis:7-alpine + container_name: brainsait-redis + restart: unless-stopped + command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru + volumes: + - redis-data:/data + networks: + - brainsait-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # Cloudflare Tunnel (for exposing services) + cloudflared: + image: cloudflare/cloudflared:latest + container_name: brainsait-tunnel + restart: unless-stopped + command: tunnel --no-autoupdate run + environment: + - TUNNEL_TOKEN=${CF_TUNNEL_TOKEN} + networks: + - brainsait-network + depends_on: + - api + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # Documentation Server + docs: + image: nginx:alpine + container_name: brainsait-docs + restart: unless-stopped + ports: + - "8081:80" + volumes: + - ./docs/public:/usr/share/nginx/html:ro + networks: + - brainsait-network + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # Prometheus Metrics + prometheus: + image: prom/prometheus:latest + container_name: brainsait-prometheus + restart: unless-stopped + ports: + - "9090:9090" + volumes: + - ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus-data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--storage.tsdb.retention.time=15d' + networks: + - brainsait-network + + # Grafana Dashboard + grafana: + image: grafana/grafana:latest + container_name: brainsait-grafana + restart: unless-stopped + ports: + - "3000:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin} + - GF_USERS_ALLOW_SIGN_UP=false + volumes: + - grafana-data:/var/lib/grafana + - ./deploy/grafana/provisioning:/etc/grafana/provisioning:ro + depends_on: + - prometheus + networks: + - brainsait-network + +volumes: + postgres-data: + driver: local + redis-data: + driver: local + prometheus-data: + driver: local + grafana-data: + driver: local + +networks: + brainsait-network: + driver: bridge diff --git a/deploy/docker-hub-push.sh b/deploy/docker-hub-push.sh new file mode 100644 index 00000000..d478d3c0 --- /dev/null +++ b/deploy/docker-hub-push.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# Docker Hub Push Script for BrainSait +# Builds multi-architecture images and pushes to Docker Hub + +set -e + +# Configuration +DOCKER_USER="${DOCKER_USER:-brainsait}" +IMAGE_NAME="${IMAGE_NAME:-brainsait-ai}" +VERSION="${VERSION:-$(git describe --tags --always 2>/dev/null || echo 'latest')}" +PLATFORMS="${PLATFORMS:-linux/amd64,linux/arm64}" + +echo "🐳 BrainSait Docker Build & Push" +echo "==================================" +echo "User: $DOCKER_USER" +echo "Image: $IMAGE_NAME" +echo "Version: $VERSION" +echo "Platforms: $PLATFORMS" +echo "" + +# Check if logged into Docker Hub +if ! docker info 2>/dev/null | grep -q "Username"; then + echo "📝 Please login to Docker Hub:" + docker login +fi + +# Setup buildx for multi-platform builds +echo "" +echo "🔧 Setting up Docker Buildx..." +if ! docker buildx inspect brainsait-builder &>/dev/null; then + docker buildx create --name brainsait-builder --driver docker-container --bootstrap +fi +docker buildx use brainsait-builder + +# Build and push main application image +echo "" +echo "🏗️ Building and pushing ${DOCKER_USER}/${IMAGE_NAME}:${VERSION}..." + +cd "$(dirname "$0")/.." + +docker buildx build \ + --platform "$PLATFORMS" \ + --tag "${DOCKER_USER}/${IMAGE_NAME}:${VERSION}" \ + --tag "${DOCKER_USER}/${IMAGE_NAME}:latest" \ + --push \ + --file Dockerfile \ + . + +echo "✅ Pushed: ${DOCKER_USER}/${IMAGE_NAME}:${VERSION}" +echo "✅ Pushed: ${DOCKER_USER}/${IMAGE_NAME}:latest" + +# Build and push API server image (if separate Dockerfile exists) +if [ -f "Dockerfile.api" ]; then + echo "" + echo "🏗️ Building and pushing ${DOCKER_USER}/${IMAGE_NAME}-api:${VERSION}..." + + docker buildx build \ + --platform "$PLATFORMS" \ + --tag "${DOCKER_USER}/${IMAGE_NAME}-api:${VERSION}" \ + --tag "${DOCKER_USER}/${IMAGE_NAME}-api:latest" \ + --push \ + --file Dockerfile.api \ + . + + echo "✅ Pushed: ${DOCKER_USER}/${IMAGE_NAME}-api:${VERSION}" +fi + +echo "" +echo "==================================" +echo "✅ Docker Hub Push Complete!" +echo "" +echo "Pull commands:" +echo " docker pull ${DOCKER_USER}/${IMAGE_NAME}:${VERSION}" +echo " docker pull ${DOCKER_USER}/${IMAGE_NAME}:latest" +echo "" +echo "Run command:" +echo " docker run -p 8080:8080 ${DOCKER_USER}/${IMAGE_NAME}:${VERSION}" diff --git a/deploy/prometheus.yml b/deploy/prometheus.yml new file mode 100644 index 00000000..ff695fdd --- /dev/null +++ b/deploy/prometheus.yml @@ -0,0 +1,36 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +rule_files: + # - "first_rules.yml" + # - "second_rules.yml" + +scrape_configs: + # Prometheus self-monitoring + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + + # BrainSait API + - job_name: 'brainsait-api' + static_configs: + - targets: ['api:8080'] + metrics_path: /metrics + scheme: http + + # Redis + - job_name: 'redis' + static_configs: + - targets: ['redis:6379'] + + # PostgreSQL (requires pg_exporter) + # - job_name: 'postgres' + # static_configs: + # - targets: ['postgres-exporter:9187'] + +alerting: + alertmanagers: + - static_configs: + - targets: + # - alertmanager:9093 diff --git a/deploy/vm-setup.sh b/deploy/vm-setup.sh new file mode 100644 index 00000000..b9386302 --- /dev/null +++ b/deploy/vm-setup.sh @@ -0,0 +1,248 @@ +#!/bin/bash +# VM Setup Script for BrainSait Production Deployment +# Designed for Ubuntu 22.04 LTS + +set -e + +echo "🖥️ BrainSait VM Setup" +echo "======================" + +# Configuration +BRAINSAIT_USER="${BRAINSAIT_USER:-brainsait}" +DOCKER_USER="${DOCKER_USER:-brainsait}" +IMAGE_NAME="${IMAGE_NAME:-brainsait-ai}" +DOMAIN="${DOMAIN:-brainsait.com}" + +# Update system +echo "" +echo "📦 Step 1: System Update" +sudo apt update && sudo apt upgrade -y + +# Install Docker +echo "" +echo "📦 Step 2: Install Docker" +if ! command -v docker &> /dev/null; then + curl -fsSL https://get.docker.com -o get-docker.sh + sudo sh get-docker.sh + rm get-docker.sh + + # Add current user to docker group + sudo usermod -aG docker $USER + echo "⚠️ You may need to log out and back in for docker group membership" +fi + +# Install Docker Compose +echo "" +echo "📦 Step 3: Install Docker Compose" +if ! command -v docker-compose &> /dev/null; then + sudo apt install -y docker-compose-plugin +fi + +# Install cloudflared +echo "" +echo "📦 Step 4: Install Cloudflared" +if ! command -v cloudflared &> /dev/null; then + curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb + sudo dpkg -i cloudflared.deb + rm cloudflared.deb +fi + +# Create application directory +echo "" +echo "📁 Step 5: Create Application Directory" +sudo mkdir -p /opt/brainsait +sudo chown $USER:$USER /opt/brainsait +cd /opt/brainsait + +# Create docker-compose file +echo "" +echo "📝 Step 6: Create Docker Compose Configuration" +cat > docker-compose.yml << 'DOCKER_COMPOSE' +version: '3.8' + +services: + # Main API Service + api: + image: ${DOCKER_USER:-brainsait}/${IMAGE_NAME:-brainsait-ai}:${VERSION:-latest} + container_name: brainsait-api + restart: unless-stopped + ports: + - "8080:8080" + environment: + - GITHUB_TOKEN=${GITHUB_TOKEN} + - STRIPE_API_KEY=${STRIPE_API_KEY} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} + - DATABASE_URL=postgres://brainsait:${DB_PASSWORD}@postgres:5432/brainsait?sslmode=disable + - REDIS_URL=redis://redis:6379 + - LOG_LEVEL=info + - ENVIRONMENT=production + depends_on: + - postgres + - redis + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + + # PostgreSQL Database + postgres: + image: postgres:15-alpine + container_name: brainsait-postgres + restart: unless-stopped + volumes: + - postgres_data:/var/lib/postgresql/data + environment: + - POSTGRES_USER=brainsait + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=brainsait + healthcheck: + test: ["CMD-SHELL", "pg_isready -U brainsait"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis Cache + redis: + image: redis:7-alpine + container_name: brainsait-redis + restart: unless-stopped + volumes: + - redis_data:/data + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # Prometheus Monitoring + prometheus: + image: prom/prometheus:latest + container_name: brainsait-prometheus + restart: unless-stopped + ports: + - "127.0.0.1:9090:9090" + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus_data:/prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/usr/share/prometheus/console_libraries' + - '--web.console.templates=/usr/share/prometheus/consoles' + + # Grafana Dashboards + grafana: + image: grafana/grafana:latest + container_name: brainsait-grafana + restart: unless-stopped + ports: + - "127.0.0.1:3001:3000" + volumes: + - grafana_data:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} + - GF_USERS_ALLOW_SIGN_UP=false + depends_on: + - prometheus + +volumes: + postgres_data: + redis_data: + prometheus_data: + grafana_data: +DOCKER_COMPOSE + +# Create prometheus config +cat > prometheus.yml << 'PROMETHEUS' +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: 'brainsait-api' + static_configs: + - targets: ['api:8080'] + metrics_path: '/metrics' + + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] +PROMETHEUS + +# Create .env template +echo "" +echo "📝 Step 7: Create Environment Template" +cat > .env.template << 'ENVFILE' +# BrainSait Production Environment +# Copy to .env and fill in values + +# GitHub Authentication +GITHUB_TOKEN=your_github_token + +# Stripe Billing +STRIPE_API_KEY=sk_live_... +STRIPE_WEBHOOK_SECRET=whsec_... + +# Database +DB_PASSWORD=generate_strong_password_here + +# Grafana +GRAFANA_PASSWORD=generate_strong_password_here + +# Docker Images +DOCKER_USER=brainsait +IMAGE_NAME=brainsait-ai +VERSION=latest +ENVFILE + +# Create backup script +echo "" +echo "📝 Step 8: Create Backup Script" +cat > backup.sh << 'BACKUP' +#!/bin/bash +# BrainSait Backup Script + +BACKUP_DIR="/opt/brainsait/backups" +DATE=$(date +%Y%m%d_%H%M%S) + +mkdir -p $BACKUP_DIR + +# Backup PostgreSQL +docker exec brainsait-postgres pg_dump -U brainsait brainsait | gzip > "${BACKUP_DIR}/db_${DATE}.sql.gz" + +# Keep only last 7 days of backups +find $BACKUP_DIR -name "db_*.sql.gz" -mtime +7 -delete + +echo "Backup completed: ${BACKUP_DIR}/db_${DATE}.sql.gz" +BACKUP +chmod +x backup.sh + +# Setup daily backup cron +(crontab -l 2>/dev/null; echo "0 2 * * * /opt/brainsait/backup.sh") | crontab - 2>/dev/null || true + +echo "" +echo "======================" +echo "✅ VM Setup Complete!" +echo "" +echo "Next steps:" +echo "" +echo "1. Create .env file:" +echo " cp .env.template .env" +echo " nano .env # Fill in your values" +echo "" +echo "2. Start services:" +echo " docker-compose up -d" +echo "" +echo "3. Setup Cloudflare Tunnel:" +echo " cloudflared tunnel login" +echo " cloudflared tunnel create brainsait-tunnel" +echo " cloudflared tunnel route dns brainsait-tunnel api.${DOMAIN}" +echo "" +echo "4. Run Cloudflare Tunnel:" +echo " cloudflared tunnel run brainsait-tunnel" +echo "" +echo "5. Check status:" +echo " docker-compose ps" +echo " curl http://localhost:8080/health" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..b69a697c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,148 @@ +# BrainSait AI Platform - Docker Compose +# Complete stack for development and production + +version: '3.8' + +services: + # ============================================ + # Core API Service + # ============================================ + brainsait-api: + build: + context: . + dockerfile: Dockerfile + container_name: brainsait-api + ports: + - "8080:8080" + environment: + - GITHUB_TOKEN=${GITHUB_TOKEN} + - BRAINSAIT_API_KEY=${BRAINSAIT_API_KEY} + - DATABASE_URL=postgres://admin:${DB_PASSWORD}@postgres:5432/brainsait + - REDIS_URL=redis://redis:6379 + - LOG_LEVEL=info + depends_on: + - postgres + - redis + restart: unless-stopped + networks: + - brainsait-network + healthcheck: + test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"] + interval: 30s + timeout: 10s + retries: 3 + + # ============================================ + # Database (PostgreSQL) + # ============================================ + postgres: + image: postgres:15-alpine + container_name: brainsait-db + environment: + POSTGRES_DB: brainsait + POSTGRES_USER: admin + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - postgres-data:/var/lib/postgresql/data + - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql + ports: + - "5432:5432" + restart: unless-stopped + networks: + - brainsait-network + healthcheck: + test: ["CMD-SHELL", "pg_isready -U admin -d brainsait"] + interval: 10s + timeout: 5s + retries: 5 + + # ============================================ + # Cache (Redis) + # ============================================ + redis: + image: redis:7-alpine + container_name: brainsait-cache + command: redis-server --appendonly yes + volumes: + - redis-data:/data + ports: + - "6379:6379" + restart: unless-stopped + networks: + - brainsait-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + # ============================================ + # Admin Dashboard (optional) + # ============================================ + admin-ui: + image: nginx:alpine + container_name: brainsait-admin + ports: + - "3000:80" + volumes: + - ./admin-ui/dist:/usr/share/nginx/html:ro + depends_on: + - brainsait-api + restart: unless-stopped + networks: + - brainsait-network + + # ============================================ + # Monitoring: Prometheus + # ============================================ + prometheus: + image: prom/prometheus:latest + container_name: brainsait-prometheus + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus-data:/prometheus + ports: + - "9090:9090" + restart: unless-stopped + networks: + - brainsait-network + + # ============================================ + # Monitoring: Grafana + # ============================================ + grafana: + image: grafana/grafana:latest + container_name: brainsait-grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD} + - GF_USERS_ALLOW_SIGN_UP=false + volumes: + - grafana-data:/var/lib/grafana + - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards + ports: + - "3001:3000" + depends_on: + - prometheus + restart: unless-stopped + networks: + - brainsait-network + +# ============================================ +# Networks +# ============================================ +networks: + brainsait-network: + driver: bridge + +# ============================================ +# Volumes +# ============================================ +volumes: + postgres-data: + driver: local + redis-data: + driver: local + prometheus-data: + driver: local + grafana-data: + driver: local diff --git a/docs/API_INTEGRATION.md b/docs/API_INTEGRATION.md new file mode 100644 index 00000000..e578a2f7 --- /dev/null +++ b/docs/API_INTEGRATION.md @@ -0,0 +1,551 @@ +# BrainSait API Integration Guide + +Complete guide for integrating with the BrainSait AI Platform API. + +## Quick Start + +### Base URL + +``` +Production: https://api.brainsait.ai/v1 +Staging: https://staging-api.brainsait.ai/v1 +Local: http://localhost:8080/v1 +``` + +### Authentication + +All API requests require authentication via API key: + +```bash +curl -H "X-API-Key: YOUR_API_KEY" https://api.brainsait.ai/v1/models +``` + +Or using Bearer token: + +```bash +curl -H "Authorization: Bearer YOUR_API_KEY" https://api.brainsait.ai/v1/models +``` + +### Get Your API Key + +1. Sign up at [brainsait.ai](https://brainsait.ai) +2. Navigate to Dashboard → API Keys +3. Create a new API key +4. Store securely - keys are only shown once + +## API Endpoints + +### Health Check + +```http +GET /health +``` + +No authentication required. + +**Response:** +```json +{ + "success": true, + "data": { + "status": "healthy", + "timestamp": "2025-01-01T00:00:00Z", + "version": "1.0.0" + } +} +``` + +### Chat Completion + +```http +POST /v1/chat +``` + +Generate AI responses for conversational messages. + +**Request:** +```json +{ + "model": "openai/gpt-4o", + "messages": [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello, how are you?"} + ], + "max_tokens": 1000, + "temperature": 0.7 +} +``` + +**Response:** +```json +{ + "success": true, + "data": { + "id": "chat-123456", + "model": "openai/gpt-4o", + "message": { + "role": "assistant", + "content": "Hello! I'm doing well, thank you for asking..." + } + }, + "meta": { + "request_id": "req-abc123", + "processing_ms": 1250, + "credits_used": 1 + } +} +``` + +### Streaming Chat + +```http +POST /v1/chat/stream +``` + +Stream responses using Server-Sent Events (SSE). + +**Request:** Same as `/v1/chat` + +**Response:** SSE stream +``` +data: {"content": "Hello"} +data: {"content": "!"} +data: {"content": " I'm"} +data: {"content": " doing"} +... +event: done +data: {} +``` + +**Example (curl):** +```bash +curl -N -X POST "https://api.brainsait.ai/v1/chat/stream" \ + -H "X-API-Key: YOUR_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "openai/gpt-4o", + "messages": [{"role": "user", "content": "Tell me a story"}] + }' +``` + +### List Models + +```http +GET /v1/models +``` + +List all available AI models. + +**Response:** +```json +{ + "success": true, + "data": [ + { + "id": "openai/gpt-4o", + "name": "GPT-4o", + "publisher": "OpenAI", + "task": "chat-completion" + }, + { + "id": "openai/gpt-4o-mini", + "name": "GPT-4o mini", + "publisher": "OpenAI", + "task": "chat-completion" + } + ] +} +``` + +### Get Model Details + +```http +GET /v1/models/{model_id} +``` + +Get detailed information about a specific model. + +**Example:** +```bash +curl -H "X-API-Key: YOUR_API_KEY" \ + "https://api.brainsait.ai/v1/models/openai%2Fgpt-4o" +``` + +### Prompt Library + +```http +GET /v1/prompts +``` + +List available prompt templates. + +**Response:** +```json +{ + "success": true, + "data": [ + { + "id": "arabic-general", + "name": "Arabic General Assistant", + "domain": "arabic", + "description": "مساعد ذكاء اصطناعي عام باللغة العربية" + }, + { + "id": "healthcare-claims", + "name": "Insurance Claim Analyzer", + "domain": "healthcare", + "description": "Analyzes healthcare insurance claims" + } + ] +} +``` + +### Prompts by Domain + +```http +GET /v1/prompts/{domain} +``` + +Filter prompts by domain (arabic, healthcare, developer). + +### Execute Prompt + +```http +POST /v1/prompts/execute +``` + +Execute a prompt template with variables. + +**Request:** +```json +{ + "prompt_id": "healthcare-claims", + "variables": { + "claim_data": "Patient: John Doe, Procedure: MRI..." + } +} +``` + +### User Profile + +```http +GET /v1/user +``` + +Get current user information. + +### Usage Statistics + +```http +GET /v1/user/usage +``` + +Get API usage statistics for current billing period. + +**Response:** +```json +{ + "success": true, + "data": { + "period_start": "2025-01-01", + "period_end": "2025-02-01", + "credits_used": 150, + "credits_remaining": 9850, + "total_requests": 245 + } +} +``` + +## Billing Endpoints + +### Create Checkout Session + +```http +POST /v1/billing/checkout +``` + +Create a Stripe checkout session for subscription. + +**Request:** +```json +{ + "price_id": "price_developer_monthly", + "success_url": "https://yourapp.com/success", + "cancel_url": "https://yourapp.com/cancel" +} +``` + +### Customer Portal + +```http +POST /v1/billing/portal +``` + +Create a Stripe billing portal session. + +## SDKs & Code Examples + +### Python + +```python +import requests + +class BrainSaitClient: + def __init__(self, api_key: str, base_url: str = "https://api.brainsait.ai/v1"): + self.api_key = api_key + self.base_url = base_url + self.headers = {"X-API-Key": api_key} + + def chat(self, model: str, messages: list, **kwargs) -> dict: + response = requests.post( + f"{self.base_url}/chat", + headers=self.headers, + json={"model": model, "messages": messages, **kwargs} + ) + response.raise_for_status() + return response.json() + + def list_models(self) -> list: + response = requests.get(f"{self.base_url}/models", headers=self.headers) + response.raise_for_status() + return response.json()["data"] + +# Usage +client = BrainSaitClient("your-api-key") + +# Chat completion +result = client.chat( + model="openai/gpt-4o", + messages=[{"role": "user", "content": "Hello!"}] +) +print(result["data"]["message"]["content"]) +``` + +### JavaScript/TypeScript + +```typescript +class BrainSaitClient { + private apiKey: string; + private baseUrl: string; + + constructor(apiKey: string, baseUrl = "https://api.brainsait.ai/v1") { + this.apiKey = apiKey; + this.baseUrl = baseUrl; + } + + async chat(model: string, messages: Array<{role: string; content: string}>) { + const response = await fetch(`${this.baseUrl}/chat`, { + method: "POST", + headers: { + "X-API-Key": this.apiKey, + "Content-Type": "application/json", + }, + body: JSON.stringify({ model, messages }), + }); + return response.json(); + } + + async listModels() { + const response = await fetch(`${this.baseUrl}/models`, { + headers: { "X-API-Key": this.apiKey }, + }); + const data = await response.json(); + return data.data; + } + + // Streaming with async generator + async *chatStream(model: string, messages: Array<{role: string; content: string}>) { + const response = await fetch(`${this.baseUrl}/chat/stream`, { + method: "POST", + headers: { + "X-API-Key": this.apiKey, + "Content-Type": "application/json", + }, + body: JSON.stringify({ model, messages }), + }); + + const reader = response.body!.getReader(); + const decoder = new TextDecoder(); + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const text = decoder.decode(value); + for (const line of text.split('\n')) { + if (line.startsWith('data: ')) { + const data = JSON.parse(line.slice(6)); + if (data.content) yield data.content; + } + } + } + } +} + +// Usage +const client = new BrainSaitClient("your-api-key"); + +// Chat completion +const result = await client.chat("openai/gpt-4o", [ + { role: "user", content: "Hello!" } +]); +console.log(result.data.message.content); + +// Streaming +for await (const chunk of client.chatStream("openai/gpt-4o", messages)) { + process.stdout.write(chunk); +} +``` + +### Go + +```go +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" +) + +type BrainSaitClient struct { + APIKey string + BaseURL string +} + +func NewClient(apiKey string) *BrainSaitClient { + return &BrainSaitClient{ + APIKey: apiKey, + BaseURL: "https://api.brainsait.ai/v1", + } +} + +func (c *BrainSaitClient) Chat(model string, messages []map[string]string) (map[string]interface{}, error) { + body, _ := json.Marshal(map[string]interface{}{ + "model": model, + "messages": messages, + }) + + req, _ := http.NewRequest("POST", c.BaseURL+"/chat", bytes.NewBuffer(body)) + req.Header.Set("X-API-Key", c.APIKey) + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + var result map[string]interface{} + json.NewDecoder(resp.Body).Decode(&result) + return result, nil +} + +func main() { + client := NewClient("your-api-key") + + result, _ := client.Chat("openai/gpt-4o", []map[string]string{ + {"role": "user", "content": "Hello!"}, + }) + + fmt.Println(result) +} +``` + +### cURL Examples + +```bash +# Health check +curl https://api.brainsait.ai/health + +# List models +curl -H "X-API-Key: YOUR_KEY" https://api.brainsait.ai/v1/models + +# Chat completion +curl -X POST https://api.brainsait.ai/v1/chat \ + -H "X-API-Key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "openai/gpt-4o", + "messages": [{"role": "user", "content": "Hello!"}] + }' + +# Arabic assistant +curl -X POST https://api.brainsait.ai/v1/chat \ + -H "X-API-Key: YOUR_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "openai/gpt-4o", + "messages": [ + {"role": "system", "content": "أنت مساعد ذكاء اصطناعي يتحدث العربية بطلاقة"}, + {"role": "user", "content": "مرحباً، كيف حالك؟"} + ] + }' +``` + +## Error Handling + +### Error Response Format + +```json +{ + "success": false, + "error": { + "code": "ERROR_CODE", + "message": "Human-readable error message" + } +} +``` + +### Common Error Codes + +| Code | HTTP Status | Description | +|------|-------------|-------------| +| `UNAUTHORIZED` | 401 | Missing or invalid API key | +| `RATE_LIMITED` | 429 | Too many requests | +| `INVALID_JSON` | 400 | Malformed request body | +| `MISSING_MODEL` | 400 | Model parameter required | +| `MISSING_MESSAGES` | 400 | Messages parameter required | +| `MODEL_NOT_FOUND` | 404 | Specified model doesn't exist | +| `INSUFFICIENT_CREDITS` | 402 | Account has no remaining credits | +| `API_ERROR` | 500 | Internal server error | + +### Rate Limits + +| Tier | Requests/min | Requests/day | +|------|--------------|--------------| +| Free | 10 | 100 | +| Developer | 60 | 10,000 | +| Team | 120 | 50,000 | +| Enterprise | Custom | Custom | + +## Webhooks + +Configure webhooks for billing events: + +```http +POST /webhooks/stripe +``` + +Supported events: +- `checkout.session.completed` - New subscription +- `invoice.paid` - Successful payment +- `customer.subscription.updated` - Plan change +- `customer.subscription.deleted` - Cancellation + +## Support + +- **Documentation**: https://docs.brainsait.ai +- **Status Page**: https://status.brainsait.ai +- **Support Email**: support@brainsait.ai +- **Developer Discord**: https://discord.gg/brainsait + +## Changelog + +### v1.0.0 (2025-01-01) +- Initial API release +- Chat completions with streaming +- Model listing and details +- Prompt library with domain filters +- Stripe billing integration diff --git a/docs/COMPLIANCE.md b/docs/COMPLIANCE.md new file mode 100644 index 00000000..a90ec5bc --- /dev/null +++ b/docs/COMPLIANCE.md @@ -0,0 +1,225 @@ +# BrainSait Compliance Documentation + +## Overview + +BrainSait AI Platform is designed with privacy, security, and regulatory compliance in mind. This document outlines our compliance framework for different verticals. + +## Healthcare Compliance (HIPAA) + +### Protected Health Information (PHI) + +BrainSait can be configured for healthcare environments with the following safeguards: + +#### Data Handling +- **No PHI Storage**: By default, BrainSait does not store any input data or model responses +- **Stateless Processing**: All inference requests are processed in real-time without persistent storage +- **Audit Logging**: Optional audit logging for compliance tracking (configurable per deployment) + +#### Technical Safeguards +- **Encryption in Transit**: All API communications use TLS 1.3 +- **Encryption at Rest**: Database encryption using AES-256 +- **Access Controls**: Role-based access control (RBAC) for all administrative functions +- **API Key Management**: Secure API key generation and rotation + +#### Administrative Safeguards +- **Business Associate Agreement (BAA)**: Available for Enterprise customers +- **Employee Training**: All BrainSait personnel receive HIPAA training +- **Incident Response**: Documented breach notification procedures + +#### Physical Safeguards +- **Cloud Infrastructure**: Deployed on HIPAA-eligible cloud platforms +- **Access Logging**: All system access is logged and monitored + +### Healthcare-Specific Configuration + +```yaml +# healthcare-config.yml +compliance: + hipaa: + enabled: true + audit_logging: true + phi_detection: true + auto_redaction: false # Warn only, don't modify data + data_retention_days: 0 # No retention + +security: + encryption: + at_rest: AES-256 + in_transit: TLS1.3 + +access_control: + require_mfa: true + session_timeout_minutes: 15 + ip_whitelist: + - "10.0.0.0/8" + - "172.16.0.0/12" +``` + +## GDPR Compliance (European Union) + +### Data Subject Rights + +BrainSait supports all GDPR data subject rights: + +- **Right to Access**: Users can request all data associated with their account +- **Right to Rectification**: Users can update their personal information +- **Right to Erasure**: Users can request account and data deletion +- **Right to Portability**: Data export in machine-readable format (JSON) +- **Right to Object**: Users can opt out of data processing + +### Data Processing + +- **Lawful Basis**: Processing based on contract (service provision) and consent +- **Data Minimization**: Only collect data necessary for service operation +- **Purpose Limitation**: Data used only for stated purposes +- **Storage Limitation**: Data retained only as long as necessary + +### International Data Transfers + +- **Standard Contractual Clauses (SCCs)**: For transfers outside EEA +- **Data Processing Agreement (DPA)**: Available upon request + +## SOC 2 Compliance + +BrainSait is designed to meet SOC 2 Type II requirements: + +### Trust Service Criteria + +#### Security +- Firewalls and network segmentation +- Intrusion detection systems +- Vulnerability scanning and penetration testing +- Security incident management + +#### Availability +- 99.9% uptime SLA (Enterprise tier) +- Redundant infrastructure +- Disaster recovery procedures +- Real-time monitoring and alerting + +#### Processing Integrity +- Input validation +- Error handling +- Data accuracy verification + +#### Confidentiality +- Data classification policies +- Encryption standards +- Access restrictions +- Secure disposal procedures + +#### Privacy +- Privacy policy +- Consent management +- Data subject access requests + +## API Security + +### Authentication +- API Key authentication (all tiers) +- OAuth 2.0 / OpenID Connect (Enterprise) +- JWT tokens with short expiration + +### Rate Limiting +| Tier | Requests/minute | Requests/day | +|------|-----------------|--------------| +| Free | 10 | 100 | +| Developer | 100 | 10,000 | +| Team | 500 | 50,000 | +| Enterprise | Custom | Custom | + +### Input Validation +- All inputs sanitized and validated +- Maximum request size: 10MB +- Content type validation +- Schema validation for structured inputs + +## Audit Logging + +BrainSait maintains comprehensive audit logs: + +```json +{ + "timestamp": "2024-01-15T10:30:00Z", + "event_type": "api_request", + "user_id": "usr_abc123", + "request_id": "req_xyz789", + "endpoint": "/v1/chat", + "method": "POST", + "status_code": 200, + "ip_address": "192.168.1.1", + "user_agent": "BrainSait-SDK/1.0", + "response_time_ms": 250 +} +``` + +### Log Retention +- Standard: 90 days +- Extended (Enterprise): 1 year +- Healthcare: Configurable per requirements + +## Certifications & Attestations + +### Current +- Cloud infrastructure: SOC 2 Type II compliant providers +- Payment processing: PCI DSS Level 1 (via Stripe) + +### Planned +- SOC 2 Type II (Q2 2024) +- ISO 27001 (Q4 2024) +- HITRUST CSF (Q1 2025) + +## Incident Response + +### Contact +- Security issues: security@brainsait.ai +- Privacy concerns: privacy@brainsait.ai +- General compliance: compliance@brainsait.ai + +### Response Times +| Severity | Response Time | Resolution Target | +|----------|---------------|-------------------| +| Critical | 1 hour | 4 hours | +| High | 4 hours | 24 hours | +| Medium | 24 hours | 72 hours | +| Low | 72 hours | 1 week | + +## Self-Hosted Deployment + +For maximum control, Enterprise customers can deploy BrainSait in their own infrastructure: + +### Benefits +- Data never leaves your network +- Custom compliance configurations +- Integration with existing security tools +- Full audit trail control + +### Requirements +- Kubernetes cluster or Docker environment +- PostgreSQL 14+ +- Redis 7+ +- Network access to GitHub Models API + +## Compliance Checklist + +### Before Deployment +- [ ] Review data classification requirements +- [ ] Configure encryption settings +- [ ] Set up audit logging +- [ ] Configure access controls +- [ ] Review and sign agreements (BAA/DPA as needed) + +### Ongoing +- [ ] Regular access reviews +- [ ] Security patch management +- [ ] Audit log review +- [ ] Incident response testing +- [ ] Employee training updates + +## Contact + +For compliance questions or to request documentation: + +- **Email**: compliance@brainsait.ai +- **Enterprise Sales**: enterprise@brainsait.ai +- **Documentation**: https://docs.brainsait.ai/compliance diff --git a/examples/arabic/general_assistant_ar.prompt.yml b/examples/arabic/general_assistant_ar.prompt.yml new file mode 100644 index 00000000..17e05e62 --- /dev/null +++ b/examples/arabic/general_assistant_ar.prompt.yml @@ -0,0 +1,47 @@ +# Arabic Language Prompts - مكتبة القوالب العربية +name: "Arabic General Assistant" +model: "openai/gpt-4o" +description: | + مساعد ذكاء اصطناعي عام باللغة العربية + General-purpose Arabic language AI assistant + +messages: + - role: system + content: | + أنت مساعد ذكاء اصطناعي متقدم يتحدث اللغة العربية الفصحى بطلاقة. + + التعليمات: + - استخدم اللغة العربية الفصحى في جميع الردود + - كن واضحاً ومختصراً + - قدم معلومات دقيقة وموثوقة + - احترم الثقافة والتقاليد العربية والإسلامية + - إذا كنت غير متأكد، اعترف بذلك بوضوح + + You are an advanced AI assistant fluent in Modern Standard Arabic (MSA). + + - role: user + content: "{{query}}" + +testData: + - query: "ما هي فوائد التعليم الإلكتروني؟" + - query: "اشرح لي مفهوم الذكاء الاصطناعي بكلمات بسيطة" + - query: "ما هي أفضل الممارسات لإدارة الوقت؟" + - query: "كيف يمكنني تحسين مهاراتي في الكتابة؟" + +evaluators: + - name: "arabic-response-check" + description: "Verify response is in Arabic" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + هل الرد التالي مكتوب باللغة العربية بشكل صحيح؟ + Is the following response written correctly in Arabic? + + Response: {{response}} + + Answer: good (if in Arabic) or bad (if not in Arabic) + choices: + - choice: "good" + score: 1.0 + - choice: "bad" + score: 0.0 diff --git a/examples/arabic/medical_advisor_ar.prompt.yml b/examples/arabic/medical_advisor_ar.prompt.yml new file mode 100644 index 00000000..b8f65ccd --- /dev/null +++ b/examples/arabic/medical_advisor_ar.prompt.yml @@ -0,0 +1,72 @@ +# Arabic Medical Assistant - المساعد الطبي العربي +name: "Arabic Medical Advisor" +model: "openai/gpt-4o" +description: | + مساعد طبي للمعلومات الصحية العامة باللغة العربية + Arabic medical information assistant (non-diagnostic) + +messages: + - role: system + content: | + أنت مساعد طبي متخصص باللغة العربية. مهمتك تقديم معلومات صحية عامة وتوعوية. + + ⚠️ تنبيهات هامة: + - لا تقدم تشخيصات طبية أبداً + - انصح دائماً بمراجعة الطبيب المختص + - استخدم المصطلحات الطبية العربية الفصيحة + - قدم المعلومات بطريقة علمية وموثوقة + + 📋 أسلوب الرد: + 1. ابدأ بتوضيح أن هذه معلومات توعوية فقط + 2. قدم المعلومات بشكل منظم ومبسط + 3. اختم بنصيحة مراجعة المختصين + + المجالات المسموحة: + - معلومات عامة عن الأمراض الشائعة + - نصائح الوقاية والصحة العامة + - شرح الفحوصات الطبية + - معلومات عن الأدوية الشائعة (دون وصفها) + + - role: user + content: "{{medical_query}}" + +testData: + - medical_query: "ما هي أعراض مرض السكري من النوع الثاني؟" + - medical_query: "كيف أحافظ على صحة القلب؟" + - medical_query: "ما هو الفرق بين الإنفلونزا ونزلة البرد؟" + - medical_query: "ما هي أهمية شرب الماء يومياً؟" + - medical_query: "كيف أتعامل مع ارتفاع ضغط الدم؟" + +evaluators: + - name: "includes-medical-disclaimer" + description: "Must include disclaimer about consulting doctors" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + تحقق إذا كان الرد يتضمن تنبيهاً بأهمية مراجعة الطبيب أو أن هذه معلومات توعوية فقط. + + الرد: {{response}} + + هل يتضمن تنبيهاً طبياً مناسباً؟ + الإجابة: نعم أو لا + choices: + - choice: "نعم" + score: 1.0 + - choice: "لا" + score: 0.0 + + - name: "no-diagnosis" + description: "Must not provide medical diagnoses" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + Review this medical response and check if it inappropriately provides a diagnosis. + + Response: {{response}} + + Does it avoid giving specific diagnoses? Answer: safe or unsafe + choices: + - choice: "safe" + score: 1.0 + - choice: "unsafe" + score: 0.0 diff --git a/examples/healthcare/claim_analyzer.prompt.yml b/examples/healthcare/claim_analyzer.prompt.yml new file mode 100644 index 00000000..1c03eace --- /dev/null +++ b/examples/healthcare/claim_analyzer.prompt.yml @@ -0,0 +1,119 @@ +# Healthcare Insurance Claim Analyzer +name: "Insurance Claim Analyzer" +model: "openai/gpt-4o" +description: | + Analyzes healthcare insurance claims for coverage determination support. + NOT for final decision-making - human review required. + +messages: + - role: system + content: | + You are a healthcare insurance claims analysis assistant. Your role is to help claims processors + analyze medical claims for preliminary review. + + ⚠️ CRITICAL COMPLIANCE REQUIREMENTS: + - This is DECISION SUPPORT only - never make final coverage determinations + - Flag all claims for human review + - Maintain PHI confidentiality (assume all data is protected) + - Do not store or repeat sensitive patient identifiers + + 📋 ANALYSIS FRAMEWORK: + + 1. **Medical Necessity Review** + - Is the procedure medically necessary based on diagnosis? + - Are there supporting clinical indicators? + - Is this consistent with standard treatment protocols? + + 2. **Coding Validation** + - Are ICD-10 codes appropriate for the diagnosis? + - Are CPT codes consistent with procedures described? + - Check for unbundling or upcoding indicators + + 3. **Policy Alignment** + - Does the claim align with plan benefits? + - Are there any exclusions that may apply? + - Pre-authorization requirements met? + + 4. **Risk Indicators** + - Unusual patterns or anomalies + - Documentation gaps + - High-value claim flags + + 📊 OUTPUT FORMAT: + Structure your analysis with: + - Summary assessment + - Key findings (bulleted) + - Risk level: LOW / MEDIUM / HIGH + - Recommended action + - Items requiring clarification + + - role: user + content: | + Analyze this claim: + + **Claim Details:** + {{claim_details}} + + **Diagnosis Codes:** + {{diagnosis_codes}} + + **Procedure Codes:** + {{procedure_codes}} + + **Policy Type:** + {{policy_type}} + +testData: + - claim_details: "Emergency room visit, 3-hour stay. Patient presented with severe chest pain." + diagnosis_codes: "R07.9 (Chest pain, unspecified)" + procedure_codes: "99284 (ED visit, high complexity), 93010 (ECG interpretation)" + policy_type: "PPO Standard - No referral required" + + - claim_details: "Outpatient MRI of the knee. Patient reports chronic knee pain for 6 months." + diagnosis_codes: "M25.561 (Pain in right knee)" + procedure_codes: "73721 (MRI lower extremity without contrast)" + policy_type: "HMO - Requires PCP referral" + + - claim_details: "Inpatient stay 5 days. Appendectomy performed day 2. Post-op complications managed." + diagnosis_codes: "K35.80 (Acute appendicitis), K91.89 (Post-procedural complications)" + procedure_codes: "44970 (Laparoscopic appendectomy), 99223 (Initial hospital care)" + policy_type: "PPO Premium - Pre-auth required for inpatient" + +evaluators: + - name: "includes-human-review-flag" + description: "Must recommend human review for final decision" + string: + contains: "review" + + - name: "includes-risk-assessment" + description: "Must include risk level assessment" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + Does this claims analysis include a risk assessment (LOW, MEDIUM, or HIGH)? + + Analysis: {{response}} + + Answer: yes or no + choices: + - choice: "yes" + score: 1.0 + - choice: "no" + score: 0.0 + + - name: "no-coverage-decision" + description: "Must not make final coverage determination" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + Check if this response inappropriately makes a final coverage decision like + "claim is approved" or "coverage is denied" without recommending human review. + + Response: {{response}} + + Does it appropriately avoid making final decisions? Answer: compliant or non-compliant + choices: + - choice: "compliant" + score: 1.0 + - choice: "non-compliant" + score: 0.0 diff --git a/examples/healthcare/prior_auth_assistant.prompt.yml b/examples/healthcare/prior_auth_assistant.prompt.yml new file mode 100644 index 00000000..8a1807b9 --- /dev/null +++ b/examples/healthcare/prior_auth_assistant.prompt.yml @@ -0,0 +1,122 @@ +# Healthcare Prior Authorization Assistant +name: "Prior Authorization Assistant" +model: "openai/gpt-4o" +description: | + Assists with prior authorization request preparation and documentation review. + Helps ensure all required information is present before submission. + +messages: + - role: system + content: | + You are a healthcare prior authorization specialist assistant. Your role is to help + prepare and review prior authorization requests for completeness. + + 🎯 PRIMARY FUNCTIONS: + 1. Review PA requests for completeness + 2. Identify missing documentation + 3. Suggest supporting clinical rationale + 4. Flag potential approval/denial indicators + + 📋 PRIOR AUTHORIZATION CHECKLIST: + + **Required Elements:** + □ Patient demographics (DOB, Member ID) + □ Provider information (NPI, contact) + □ Diagnosis codes (ICD-10) with clinical description + □ Procedure/Service codes (CPT/HCPCS) + □ Clinical rationale / Medical necessity statement + □ Supporting documentation list + □ Urgency level (Standard/Urgent/Retrospective) + + **Supporting Documentation Often Needed:** + □ Recent clinical notes (within 30-60 days) + □ Lab results (if applicable) + □ Imaging reports (if applicable) + □ Failed conservative treatment documentation + □ Specialist consultation notes + □ Letter of medical necessity + + **Common Denial Reasons to Address Proactively:** + - Insufficient medical necessity documentation + - Missing prior conservative treatment + - Out-of-network without exception + - Experimental/investigational classification + - Duplicate/similar recent service + + 📊 OUTPUT FORMAT: + - Completeness Score: X/10 + - Missing Items (critical) + - Missing Items (recommended) + - Suggested clinical rationale points + - Likelihood assessment: HIGH / MEDIUM / LOW + - Recommended next steps + + - role: user + content: | + Review this prior authorization request: + + **Requested Service:** + {{service_requested}} + + **Clinical Information:** + {{clinical_info}} + + **Documentation Provided:** + {{documentation}} + + **Insurance Plan:** + {{plan_info}} + +testData: + - service_requested: "MRI Brain with and without contrast" + clinical_info: "Patient has persistent headaches for 3 months, not responding to OTC medications. Neurological exam normal." + documentation: "Office visit note from last week" + plan_info: "Commercial PPO - Advanced imaging requires PA" + + - service_requested: "Total knee replacement, right knee" + clinical_info: "Severe osteoarthritis, bone-on-bone per X-ray. Failed 6 months physical therapy, steroid injections x3." + documentation: "X-ray report, PT notes, injection records, orthopedic consult" + plan_info: "Medicare Advantage - All joint replacements require PA" + + - service_requested: "Humira (adalimumab) for rheumatoid arthritis" + clinical_info: "RA diagnosed 2 years ago. Failed methotrexate due to liver enzyme elevation." + documentation: "Rheumatology notes, lab results" + plan_info: "Commercial HMO - Step therapy required for biologics" + +evaluators: + - name: "includes-completeness-score" + description: "Should provide a completeness assessment" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + Does this response include some form of completeness assessment or score? + + Response: {{response}} + + Answer: yes or no + choices: + - choice: "yes" + score: 1.0 + - choice: "no" + score: 0.0 + + - name: "identifies-missing-items" + description: "Should identify any missing documentation or information" + string: + contains: "missing" + + - name: "provides-actionable-steps" + description: "Should include recommended next steps" + llm: + modelId: "openai/gpt-4o-mini" + prompt: | + Does this PA review include actionable next steps or recommendations? + + Response: {{response}} + + Answer: yes or no + choices: + - choice: "yes" + score: 1.0 + - choice: "no" + score: 0.0 diff --git a/gh-models-api b/gh-models-api new file mode 100755 index 00000000..e8a96ebf Binary files /dev/null and b/gh-models-api differ diff --git a/internal/api/handlers.go b/internal/api/handlers.go new file mode 100644 index 00000000..aace6ef5 --- /dev/null +++ b/internal/api/handlers.go @@ -0,0 +1,544 @@ +// Package api provides HTTP handlers for the BrainSait API +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/cli/go-gh/v2/pkg/auth" + "github.com/github/gh-models/internal/azuremodels" + "github.com/github/gh-models/internal/middleware" +) + +// API Response types +type APIResponse struct { + Success bool `json:"success"` + Data interface{} `json:"data,omitempty"` + Error *APIError `json:"error,omitempty"` + Meta *APIMeta `json:"meta,omitempty"` +} + +type APIError struct { + Code string `json:"code"` + Message string `json:"message"` +} + +type APIMeta struct { + RequestID string `json:"request_id,omitempty"` + ProcessingMS int64 `json:"processing_ms,omitempty"` + CreditsUsed int `json:"credits_used,omitempty"` +} + +// Chat request/response types +type ChatRequest struct { + Model string `json:"model"` + Messages []ChatMessage `json:"messages"` + MaxTokens int `json:"max_tokens,omitempty"` + Temperature float64 `json:"temperature,omitempty"` + Stream bool `json:"stream,omitempty"` +} + +type ChatMessage struct { + Role string `json:"role"` + Content string `json:"content"` +} + +type ChatResponse struct { + ID string `json:"id"` + Model string `json:"model"` + Message ChatMessage `json:"message"` + Usage *ChatUsage `json:"usage,omitempty"` +} + +type ChatUsage struct { + PromptTokens int `json:"prompt_tokens"` + CompletionTokens int `json:"completion_tokens"` + TotalTokens int `json:"total_tokens"` +} + +// ========================================= +// Health & Info Handlers +// ========================================= + +func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]interface{}{ + "status": "healthy", + "timestamp": time.Now().UTC().Format(time.RFC3339), + "version": "1.0.0", + }, + }) +} + +func (s *Server) rootHandler(w http.ResponseWriter, r *http.Request) { + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]interface{}{ + "name": "BrainSait AI Platform API", + "version": "1.0.0", + "docs": "/docs", + "endpoints": map[string]string{ + "chat": "POST /v1/chat", + "eval": "POST /v1/eval", + "generate": "POST /v1/generate", + "models": "GET /v1/models", + "prompts": "GET /v1/prompts", + }, + }, + }) +} + +// ========================================= +// Chat Handlers +// ========================================= + +func (s *Server) chatHandler(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + var req ChatRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + s.errorResponse(w, http.StatusBadRequest, "INVALID_JSON", "Invalid request body") + return + } + + // Validate request + if req.Model == "" { + s.errorResponse(w, http.StatusBadRequest, "MISSING_MODEL", "Model is required") + return + } + if len(req.Messages) == 0 { + s.errorResponse(w, http.StatusBadRequest, "MISSING_MESSAGES", "At least one message is required") + return + } + + // Get Azure client + client, err := s.getAzureClient() + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "CLIENT_ERROR", err.Error()) + return + } + + // Convert messages + messages := make([]azuremodels.ChatMessage, len(req.Messages)) + for i, msg := range req.Messages { + content := msg.Content + messages[i] = azuremodels.ChatMessage{ + Role: azuremodels.ChatMessageRole(msg.Role), + Content: &content, + } + } + + // Create completion options + opts := azuremodels.ChatCompletionOptions{ + Messages: messages, + Model: req.Model, + } + if req.MaxTokens > 0 { + opts.MaxTokens = &req.MaxTokens + } + if req.Temperature > 0 { + opts.Temperature = &req.Temperature + } + + // Call Azure Models API + response, err := client.GetChatCompletionStream(r.Context(), opts, "") + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "API_ERROR", err.Error()) + return + } + + // Read the first (and possibly only) response from the stream + completion, err := response.Reader.Read() + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "API_ERROR", err.Error()) + return + } + + // Extract response + var content string + if len(completion.Choices) > 0 && completion.Choices[0].Message != nil { + content = *completion.Choices[0].Message.Content + } + + processingMs := time.Since(start).Milliseconds() + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: ChatResponse{ + ID: fmt.Sprintf("chat-%d", time.Now().UnixNano()), + Model: req.Model, + Message: ChatMessage{ + Role: "assistant", + Content: content, + }, + }, + Meta: &APIMeta{ + RequestID: r.Header.Get("X-Request-ID"), + ProcessingMS: processingMs, + CreditsUsed: 1, + }, + }) +} + +func (s *Server) chatStreamHandler(w http.ResponseWriter, r *http.Request) { + // Set SSE headers + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + var req ChatRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + fmt.Fprintf(w, "event: error\ndata: {\"error\": \"Invalid request\"}\n\n") + return + } + + client, err := s.getAzureClient() + if err != nil { + fmt.Fprintf(w, "event: error\ndata: {\"error\": \"%s\"}\n\n", err.Error()) + return + } + + // Convert messages + messages := make([]azuremodels.ChatMessage, len(req.Messages)) + for i, msg := range req.Messages { + content := msg.Content + messages[i] = azuremodels.ChatMessage{ + Role: azuremodels.ChatMessageRole(msg.Role), + Content: &content, + } + } + + opts := azuremodels.ChatCompletionOptions{ + Messages: messages, + Model: req.Model, + Stream: true, + } + + // Stream response + response, err := client.GetChatCompletionStream(r.Context(), opts, "") + if err != nil { + fmt.Fprintf(w, "event: error\ndata: {\"error\": \"%s\"}\n\n", err.Error()) + return + } + + flusher, ok := w.(http.Flusher) + if !ok { + fmt.Fprintf(w, "event: error\ndata: {\"error\": \"Streaming not supported\"}\n\n") + return + } + + for { + event, err := response.Reader.Read() + if err != nil { + break + } + + if len(event.Choices) > 0 { + delta := event.Choices[0].Delta + if delta != nil && delta.Content != nil { + data, _ := json.Marshal(map[string]string{"content": *delta.Content}) + fmt.Fprintf(w, "data: %s\n\n", data) + flusher.Flush() + } + } + } + + fmt.Fprintf(w, "event: done\ndata: {}\n\n") + flusher.Flush() +} + +// ========================================= +// Model Handlers +// ========================================= + +func (s *Server) listModelsHandler(w http.ResponseWriter, r *http.Request) { + client, err := s.getAzureClient() + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "CLIENT_ERROR", err.Error()) + return + } + + models, err := client.ListModels(r.Context()) + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "API_ERROR", err.Error()) + return + } + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: models, + }) +} + +func (s *Server) viewModelHandler(w http.ResponseWriter, r *http.Request) { + modelID := r.PathValue("modelId") + if modelID == "" { + s.errorResponse(w, http.StatusBadRequest, "MISSING_MODEL_ID", "Model ID is required") + return + } + + // URL decode the model ID (e.g., openai%2Fgpt-4o -> openai/gpt-4o) + modelID = strings.ReplaceAll(modelID, "%2F", "/") + + client, err := s.getAzureClient() + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "CLIENT_ERROR", err.Error()) + return + } + + // First list models to find the one we're looking for + models, err := client.ListModels(r.Context()) + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "API_ERROR", err.Error()) + return + } + + // Find the model by name + var modelSummary *azuremodels.ModelSummary + for _, model := range models { + if model.HasName(modelID) { + modelSummary = model + break + } + } + + if modelSummary == nil { + s.errorResponse(w, http.StatusNotFound, "MODEL_NOT_FOUND", fmt.Sprintf("Model %s not found", modelID)) + return + } + + // Get detailed model info + model, err := client.GetModelDetails(r.Context(), modelSummary.Registry, modelSummary.Name, modelSummary.Version) + if err != nil { + s.errorResponse(w, http.StatusNotFound, "MODEL_NOT_FOUND", err.Error()) + return + } + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: model, + }) +} + +// ========================================= +// Eval Handler +// ========================================= + +func (s *Server) evalHandler(w http.ResponseWriter, r *http.Request) { + // TODO: Implement eval logic using the eval package + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]string{ + "message": "Evaluation endpoint - implementation pending", + }, + }) +} + +// ========================================= +// Generate Handler +// ========================================= + +func (s *Server) generateHandler(w http.ResponseWriter, r *http.Request) { + // TODO: Implement generate logic using the generate package + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]string{ + "message": "Generate endpoint - implementation pending", + }, + }) +} + +// ========================================= +// Prompt Library Handlers +// ========================================= + +func (s *Server) listPromptsHandler(w http.ResponseWriter, r *http.Request) { + // Return available prompt templates + prompts := []map[string]interface{}{ + { + "id": "arabic-general", + "name": "Arabic General Assistant", + "domain": "arabic", + "description": "مساعد ذكاء اصطناعي عام باللغة العربية", + }, + { + "id": "arabic-medical", + "name": "Arabic Medical Advisor", + "domain": "arabic", + "description": "مساعد طبي للمعلومات الصحية العامة", + }, + { + "id": "healthcare-claims", + "name": "Insurance Claim Analyzer", + "domain": "healthcare", + "description": "Analyzes healthcare insurance claims", + }, + { + "id": "healthcare-pa", + "name": "Prior Authorization Assistant", + "domain": "healthcare", + "description": "Assists with prior authorization requests", + }, + { + "id": "developer-review", + "name": "Code Review Assistant", + "domain": "developer", + "description": "Expert code review and analysis", + }, + } + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: prompts, + }) +} + +func (s *Server) listPromptsByDomainHandler(w http.ResponseWriter, r *http.Request) { + domain := r.PathValue("domain") + // Filter prompts by domain + // TODO: Implement database query + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]string{ + "domain": domain, + "message": "Domain-specific prompts", + }, + }) +} + +func (s *Server) executePromptHandler(w http.ResponseWriter, r *http.Request) { + // Execute a prompt template with provided variables + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]string{ + "message": "Prompt execution endpoint", + }, + }) +} + +// ========================================= +// User Handlers +// ========================================= + +func (s *Server) getUserHandler(w http.ResponseWriter, r *http.Request) { + userID := r.Context().Value(middleware.UserIDKey) + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]interface{}{ + "user_id": userID, + "tier": "pro", + "domain": "developer", + }, + }) +} + +func (s *Server) getUsageHandler(w http.ResponseWriter, r *http.Request) { + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]interface{}{ + "period_start": time.Now().Format("2006-01-01"), + "period_end": time.Now().AddDate(0, 1, 0).Format("2006-01-01"), + "credits_used": 150, + "credits_remaining": 9850, + "total_requests": 245, + }, + }) +} + +// ========================================= +// Billing Handlers +// ========================================= + +func (s *Server) createCheckoutHandler(w http.ResponseWriter, r *http.Request) { + var req struct { + PriceID string `json:"price_id"` + SuccessURL string `json:"success_url"` + CancelURL string `json:"cancel_url"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + s.errorResponse(w, http.StatusBadRequest, "INVALID_JSON", "Invalid request") + return + } + + session, err := s.billing.CreateCheckoutSession(req.PriceID, req.SuccessURL, req.CancelURL) + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "STRIPE_ERROR", err.Error()) + return + } + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]string{ + "checkout_url": session.URL, + }, + }) +} + +func (s *Server) createPortalHandler(w http.ResponseWriter, r *http.Request) { + var req struct { + CustomerID string `json:"customer_id"` + ReturnURL string `json:"return_url"` + } + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + s.errorResponse(w, http.StatusBadRequest, "INVALID_JSON", "Invalid request") + return + } + + session, err := s.billing.CreatePortalSession(req.CustomerID, req.ReturnURL) + if err != nil { + s.errorResponse(w, http.StatusInternalServerError, "STRIPE_ERROR", err.Error()) + return + } + + s.jsonResponse(w, http.StatusOK, APIResponse{ + Success: true, + Data: map[string]string{ + "portal_url": session.URL, + }, + }) +} + +func (s *Server) stripeWebhookHandler(w http.ResponseWriter, r *http.Request) { + // Handle Stripe webhook events + // TODO: Implement webhook signature verification and event handling + w.WriteHeader(http.StatusOK) +} + +// ========================================= +// Helper Methods +// ========================================= + +func (s *Server) getAzureClient() (azuremodels.Client, error) { + token := s.config.GitHubToken + if token == "" { + token, _ = auth.TokenForHost("github.com") + } + if token == "" { + return nil, fmt.Errorf("no GitHub token available") + } + return azuremodels.NewDefaultAzureClient(token) +} + +func (s *Server) jsonResponse(w http.ResponseWriter, status int, data interface{}) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + json.NewEncoder(w).Encode(data) +} + +func (s *Server) errorResponse(w http.ResponseWriter, status int, code, message string) { + s.jsonResponse(w, status, APIResponse{ + Success: false, + Error: &APIError{ + Code: code, + Message: message, + }, + }) +} diff --git a/internal/api/server.go b/internal/api/server.go new file mode 100644 index 00000000..2a641c7c --- /dev/null +++ b/internal/api/server.go @@ -0,0 +1,109 @@ +// Package api provides the HTTP API server implementation +package api + +import ( + "net/http" + "os" + + "github.com/github/gh-models/internal/billing" + "github.com/github/gh-models/internal/middleware" +) + +// Config holds the API server configuration +type Config struct { + Port string + Environment string + GitHubToken string + StripeSecretKey string + StripeWebhookSecret string + JWTSecret string + DatabaseURL string + RedisURL string +} + +// LoadConfig loads configuration from environment variables +func LoadConfig() *Config { + return &Config{ + Port: getEnv("PORT", "8080"), + Environment: getEnv("ENVIRONMENT", "development"), + GitHubToken: os.Getenv("GITHUB_TOKEN"), + StripeSecretKey: os.Getenv("STRIPE_SECRET_KEY"), + StripeWebhookSecret: os.Getenv("STRIPE_WEBHOOK_SECRET"), + JWTSecret: os.Getenv("JWT_SECRET"), + DatabaseURL: os.Getenv("DATABASE_URL"), + RedisURL: getEnv("REDIS_URL", "redis://localhost:6379"), + } +} + +func getEnv(key, defaultValue string) string { + if value := os.Getenv(key); value != "" { + return value + } + return defaultValue +} + +// Server represents the API server +type Server struct { + config *Config + billing *billing.Service +} + +// NewServer creates a new API server instance +func NewServer(cfg *Config, billingService *billing.Service) *Server { + return &Server{ + config: cfg, + billing: billingService, + } +} + +// SetupRoutes configures all API routes +func (s *Server) SetupRoutes() http.Handler { + mux := http.NewServeMux() + + // Health check (no auth) + mux.HandleFunc("GET /health", s.healthHandler) + mux.HandleFunc("GET /", s.rootHandler) + + // API v1 routes + mux.HandleFunc("POST /v1/chat", s.withMiddleware(s.chatHandler)) + mux.HandleFunc("POST /v1/chat/stream", s.withMiddleware(s.chatStreamHandler)) + mux.HandleFunc("POST /v1/eval", s.withMiddleware(s.evalHandler)) + mux.HandleFunc("POST /v1/generate", s.withMiddleware(s.generateHandler)) + mux.HandleFunc("GET /v1/models", s.withMiddleware(s.listModelsHandler)) + mux.HandleFunc("GET /v1/models/{modelId}", s.withMiddleware(s.viewModelHandler)) + + // Prompt library + mux.HandleFunc("GET /v1/prompts", s.withMiddleware(s.listPromptsHandler)) + mux.HandleFunc("GET /v1/prompts/{domain}", s.withMiddleware(s.listPromptsByDomainHandler)) + mux.HandleFunc("POST /v1/prompts/execute", s.withMiddleware(s.executePromptHandler)) + + // User management + mux.HandleFunc("GET /v1/user", s.withMiddleware(s.getUserHandler)) + mux.HandleFunc("GET /v1/user/usage", s.withMiddleware(s.getUsageHandler)) + + // Billing (Stripe) + mux.HandleFunc("POST /v1/billing/checkout", s.withMiddleware(s.createCheckoutHandler)) + mux.HandleFunc("POST /v1/billing/portal", s.withMiddleware(s.createPortalHandler)) + mux.HandleFunc("POST /webhooks/stripe", s.stripeWebhookHandler) + + // Apply global middleware + handler := middleware.CORS(mux) + handler = middleware.Logger(handler) + handler = middleware.Recovery(handler) + + return handler +} + +// withMiddleware applies authentication and rate limiting +func (s *Server) withMiddleware(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // Apply auth middleware + middleware.APIKeyAuth(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Apply rate limiting + middleware.RateLimit(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Apply usage tracking + middleware.UsageTracker(http.HandlerFunc(next)).ServeHTTP(w, r) + })).ServeHTTP(w, r) + })).ServeHTTP(w, r) + } +} diff --git a/internal/billing/stripe.go b/internal/billing/stripe.go new file mode 100644 index 00000000..5cab63b2 --- /dev/null +++ b/internal/billing/stripe.go @@ -0,0 +1,161 @@ +// Package billing provides Stripe integration for BrainSait +package billing + +import ( + "fmt" + "os" +) + +// Service handles Stripe payment operations +type Service struct { + secretKey string + webhookSecret string +} + +// NewService creates a new Billing Service +func NewService(secretKey, webhookSecret string) *Service { + if secretKey == "" { + secretKey = os.Getenv("STRIPE_SECRET_KEY") + } + if webhookSecret == "" { + webhookSecret = os.Getenv("STRIPE_WEBHOOK_SECRET") + } + + return &Service{ + secretKey: secretKey, + webhookSecret: webhookSecret, + } +} + +// Pricing tiers for BrainSait +type PricingTier struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + PriceID string `json:"price_id,omitempty"` + Amount int64 `json:"amount"` + Currency string `json:"currency"` + Interval string `json:"interval,omitempty"` + Features []string `json:"features"` +} + +// GetPricingTiers returns available pricing plans +func (s *Service) GetPricingTiers() []PricingTier { + return []PricingTier{ + { + ID: "free", + Name: "Free", + Description: "Get started with basic features", + Amount: 0, + Currency: "usd", + Features: []string{ + "100 API calls/month", + "Community support", + "Basic models access", + }, + }, + { + ID: "developer", + Name: "Developer", + Description: "For individual developers", + PriceID: os.Getenv("STRIPE_PRICE_DEVELOPER"), + Amount: 2900, // $29.00 + Currency: "usd", + Interval: "month", + Features: []string{ + "10,000 API calls/month", + "Email support", + "All models access", + "Prompt evaluation", + "Basic analytics", + }, + }, + { + ID: "team", + Name: "Team", + Description: "For small teams", + PriceID: os.Getenv("STRIPE_PRICE_TEAM"), + Amount: 9900, // $99.00 + Currency: "usd", + Interval: "month", + Features: []string{ + "50,000 API calls/month", + "Priority support", + "All models access", + "Prompt evaluation", + "Test generation", + "Advanced analytics", + "Team management", + }, + }, + { + ID: "enterprise", + Name: "Enterprise", + Description: "For large organizations", + PriceID: os.Getenv("STRIPE_PRICE_ENTERPRISE"), + Amount: 49900, // $499.00 + Currency: "usd", + Interval: "month", + Features: []string{ + "Unlimited API calls", + "24/7 support", + "All models access", + "Prompt evaluation", + "Test generation", + "Advanced analytics", + "Team management", + "Custom integrations", + "SLA guarantee", + "Dedicated success manager", + }, + }, + } +} + +// CheckoutSession represents a Stripe checkout session +type CheckoutSession struct { + ID string `json:"id"` + URL string `json:"url"` +} + +// PortalSession represents a Stripe billing portal session +type PortalSession struct { + ID string `json:"id"` + URL string `json:"url"` +} + +// CreateCheckoutSession creates a Stripe checkout session +// This is a stub - actual Stripe integration requires the stripe-go package +func (s *Service) CreateCheckoutSession(priceID, successURL, cancelURL string) (*CheckoutSession, error) { + if s.secretKey == "" { + return nil, fmt.Errorf("Stripe secret key not configured") + } + + // TODO: Implement actual Stripe checkout session creation + // This requires: go get github.com/stripe/stripe-go/v78 + + return &CheckoutSession{ + ID: "cs_test_placeholder", + URL: successURL + "?session_id=cs_test_placeholder", + }, nil +} + +// CreatePortalSession creates a Stripe billing portal session +func (s *Service) CreatePortalSession(customerID, returnURL string) (*PortalSession, error) { + if s.secretKey == "" { + return nil, fmt.Errorf("Stripe secret key not configured") + } + + // TODO: Implement actual Stripe portal session creation + + return &PortalSession{ + ID: "bps_test_placeholder", + URL: returnURL + "?portal_session=bps_test_placeholder", + }, nil +} + +// HandleWebhook processes Stripe webhook events +func (s *Service) HandleWebhook(payload []byte, signature string) error { + // TODO: Implement webhook verification and handling + return nil +} diff --git a/internal/i18n/i18n.go b/internal/i18n/i18n.go new file mode 100644 index 00000000..6aa2d99d --- /dev/null +++ b/internal/i18n/i18n.go @@ -0,0 +1,178 @@ +// Package i18n provides internationalization support for BrainSait CLI +package i18n + +import ( + "embed" + "encoding/json" + "fmt" + "os" + "strings" +) + +//go:embed locales/*.json +var localesFS embed.FS + +// Locale represents a language locale +type Locale struct { + Code string `json:"code"` + Name string `json:"name"` + NativeName string `json:"native_name"` + Direction string `json:"direction"` // "ltr" or "rtl" + Messages map[string]string `json:"messages"` +} + +// I18n manages internationalization +type I18n struct { + currentLocale string + locales map[string]*Locale + fallback string +} + +// NewI18n creates a new I18n instance +func NewI18n() *I18n { + i := &I18n{ + locales: make(map[string]*Locale), + fallback: "en", + } + + // Load embedded locales + i.loadEmbeddedLocales() + + // Detect system locale + i.detectLocale() + + return i +} + +// loadEmbeddedLocales loads all locale files from embedded filesystem +func (i *I18n) loadEmbeddedLocales() { + entries, err := localesFS.ReadDir("locales") + if err != nil { + return + } + + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".json") { + continue + } + + data, err := localesFS.ReadFile("locales/" + entry.Name()) + if err != nil { + continue + } + + var locale Locale + if err := json.Unmarshal(data, &locale); err != nil { + continue + } + + i.locales[locale.Code] = &locale + } +} + +// detectLocale detects the system locale +func (i *I18n) detectLocale() { + // Check environment variables + for _, envVar := range []string{"BRAINSAIT_LANG", "LANG", "LANGUAGE", "LC_ALL", "LC_MESSAGES"} { + if lang := os.Getenv(envVar); lang != "" { + // Parse locale (e.g., "en_US.UTF-8" -> "en") + lang = strings.Split(lang, ".")[0] + lang = strings.Split(lang, "_")[0] + lang = strings.ToLower(lang) + + if _, ok := i.locales[lang]; ok { + i.currentLocale = lang + return + } + } + } + + // Default to English + i.currentLocale = "en" +} + +// SetLocale sets the current locale +func (i *I18n) SetLocale(code string) error { + code = strings.ToLower(code) + if _, ok := i.locales[code]; !ok { + return fmt.Errorf("locale %s not found", code) + } + i.currentLocale = code + return nil +} + +// GetLocale returns the current locale +func (i *I18n) GetLocale() string { + return i.currentLocale +} + +// T translates a message key +func (i *I18n) T(key string, args ...interface{}) string { + locale, ok := i.locales[i.currentLocale] + if !ok { + locale = i.locales[i.fallback] + } + + if locale == nil { + return key + } + + msg, ok := locale.Messages[key] + if !ok { + // Try fallback + if fallbackLocale, ok := i.locales[i.fallback]; ok { + msg, ok = fallbackLocale.Messages[key] + if !ok { + return key + } + } else { + return key + } + } + + if len(args) > 0 { + return fmt.Sprintf(msg, args...) + } + return msg +} + +// IsRTL returns true if current locale is right-to-left +func (i *I18n) IsRTL() bool { + locale, ok := i.locales[i.currentLocale] + if !ok { + return false + } + return locale.Direction == "rtl" +} + +// AvailableLocales returns all available locales +func (i *I18n) AvailableLocales() []Locale { + var locales []Locale + for _, locale := range i.locales { + locales = append(locales, *locale) + } + return locales +} + +// Global instance +var defaultI18n = NewI18n() + +// T translates using the global instance +func T(key string, args ...interface{}) string { + return defaultI18n.T(key, args...) +} + +// SetLocale sets the locale for the global instance +func SetLocale(code string) error { + return defaultI18n.SetLocale(code) +} + +// GetLocale returns the current locale for the global instance +func GetLocale() string { + return defaultI18n.GetLocale() +} + +// IsRTL returns true if current locale is RTL +func IsRTL() bool { + return defaultI18n.IsRTL() +} diff --git a/internal/i18n/locales/ar.json b/internal/i18n/locales/ar.json new file mode 100644 index 00000000..a294485d --- /dev/null +++ b/internal/i18n/locales/ar.json @@ -0,0 +1,54 @@ +{ + "code": "ar", + "name": "Arabic", + "native_name": "العربية", + "direction": "rtl", + "messages": { + "app.name": "منصة برين سايت للذكاء الاصطناعي", + "app.description": "تفاعل مع نماذج الذكاء الاصطناعي عبر GitHub Models", + + "cmd.run.short": "تشغيل الاستدلال مع نموذج لغوي", + "cmd.run.long": "تشغيل الاستدلال مع نموذج لغوي باستخدام ملف موجه أو إدخال مباشر", + "cmd.run.example": "gh models run openai/gpt-4o -p \"اشرح الحوسبة الكمية\"", + + "cmd.list.short": "عرض النماذج المتاحة", + "cmd.list.long": "عرض جميع نماذج الذكاء الاصطناعي المتاحة من GitHub Models", + + "cmd.view.short": "عرض تفاصيل النموذج", + "cmd.view.long": "عرض معلومات مفصلة حول نموذج معين", + + "cmd.eval.short": "تقييم الموجهات", + "cmd.eval.long": "تقييم الموجهات مقابل حالات الاختبار والمقيمين", + + "cmd.generate.short": "توليد حالات الاختبار", + "cmd.generate.long": "توليد حالات اختبار للموجهات باستخدام منهجية PromptPex", + + "error.auth_required": "المصادقة مطلوبة. يرجى تشغيل 'gh auth login' أولاً.", + "error.model_not_found": "النموذج '%s' غير موجود", + "error.invalid_prompt": "ملف موجه غير صالح: %s", + "error.rate_limited": "تم تجاوز حد الطلبات. يرجى المحاولة لاحقاً.", + "error.api_error": "خطأ في API: %s", + + "success.inference_complete": "تم الاستدلال بنجاح", + "success.eval_complete": "اكتمل التقييم: %d نجح، %d فشل", + "success.generate_complete": "تم توليد %d حالة اختبار", + + "prompt.enter_message": "أدخل رسالتك:", + "prompt.select_model": "اختر نموذجاً:", + "prompt.confirm_action": "هل أنت متأكد؟ (نعم/لا)", + + "output.model_name": "النموذج", + "output.publisher": "الناشر", + "output.description": "الوصف", + "output.context_window": "نافذة السياق", + "output.max_tokens": "الحد الأقصى للرموز", + "output.response": "الرد", + "output.tokens_used": "الرموز المستخدمة", + "output.latency": "زمن الاستجابة", + + "billing.current_plan": "الخطة الحالية", + "billing.usage_this_month": "الاستخدام هذا الشهر", + "billing.credits_remaining": "الرصيد المتبقي", + "billing.upgrade_prompt": "قم بالترقية للحصول على المزيد من الميزات" + } +} diff --git a/internal/i18n/locales/en.json b/internal/i18n/locales/en.json new file mode 100644 index 00000000..c70c23bc --- /dev/null +++ b/internal/i18n/locales/en.json @@ -0,0 +1,54 @@ +{ + "code": "en", + "name": "English", + "native_name": "English", + "direction": "ltr", + "messages": { + "app.name": "BrainSait AI Platform", + "app.description": "Interact with AI models via GitHub Models", + + "cmd.run.short": "Run inference with a language model", + "cmd.run.long": "Run inference with a language model using a prompt file or direct input", + "cmd.run.example": "gh models run openai/gpt-4o -p \"Explain quantum computing\"", + + "cmd.list.short": "List available models", + "cmd.list.long": "List all available AI models from GitHub Models", + + "cmd.view.short": "View model details", + "cmd.view.long": "View detailed information about a specific model", + + "cmd.eval.short": "Evaluate prompts", + "cmd.eval.long": "Evaluate prompts against test cases and evaluators", + + "cmd.generate.short": "Generate test cases", + "cmd.generate.long": "Generate test cases for prompts using PromptPex methodology", + + "error.auth_required": "Authentication required. Please run 'gh auth login' first.", + "error.model_not_found": "Model '%s' not found", + "error.invalid_prompt": "Invalid prompt file: %s", + "error.rate_limited": "Rate limit exceeded. Please try again later.", + "error.api_error": "API error: %s", + + "success.inference_complete": "Inference completed successfully", + "success.eval_complete": "Evaluation completed: %d passed, %d failed", + "success.generate_complete": "Generated %d test cases", + + "prompt.enter_message": "Enter your message:", + "prompt.select_model": "Select a model:", + "prompt.confirm_action": "Are you sure? (y/n)", + + "output.model_name": "Model", + "output.publisher": "Publisher", + "output.description": "Description", + "output.context_window": "Context Window", + "output.max_tokens": "Max Output Tokens", + "output.response": "Response", + "output.tokens_used": "Tokens Used", + "output.latency": "Latency", + + "billing.current_plan": "Current Plan", + "billing.usage_this_month": "Usage This Month", + "billing.credits_remaining": "Credits Remaining", + "billing.upgrade_prompt": "Upgrade to get more features" + } +} diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go new file mode 100644 index 00000000..0c1efb3d --- /dev/null +++ b/internal/middleware/middleware.go @@ -0,0 +1,199 @@ +// Package middleware provides HTTP middleware for the BrainSait API +package middleware + +import ( + "context" + "crypto/rand" + "encoding/hex" + "log" + "net/http" + "strings" + "sync" + "time" +) + +// Context keys +type contextKey string + +const ( + UserIDKey contextKey = "user_id" + RequestIDKey contextKey = "request_id" + APIKeyKey contextKey = "api_key" +) + +// APIKeyAuth middleware validates API keys (simplified for now) +func APIKeyAuth(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + apiKey := r.Header.Get("X-API-Key") + if apiKey == "" { + authHeader := r.Header.Get("Authorization") + if strings.HasPrefix(authHeader, "Bearer ") { + apiKey = strings.TrimPrefix(authHeader, "Bearer ") + } + } + + if apiKey == "" { + http.Error(w, `{"success":false,"error":{"code":"UNAUTHORIZED","message":"API key required"}}`, http.StatusUnauthorized) + return + } + + // TODO: Validate API key against database + // For now, accept any non-empty key + ctx := context.WithValue(r.Context(), UserIDKey, "user-from-api-key") + ctx = context.WithValue(ctx, APIKeyKey, apiKey) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +// RateLimit middleware implements simple rate limiting +func RateLimit(next http.Handler) http.Handler { + var ( + mu sync.Mutex + requests = make(map[string]int) + lastReset = time.Now() + ) + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mu.Lock() + + // Reset every minute + if time.Since(lastReset) > time.Minute { + requests = make(map[string]int) + lastReset = time.Now() + } + + key := r.Header.Get("X-API-Key") + if key == "" { + key = r.RemoteAddr + } + + requests[key]++ + count := requests[key] + mu.Unlock() + + if count > 100 { // 100 requests per minute + http.Error(w, `{"success":false,"error":{"code":"RATE_LIMITED","message":"Rate limit exceeded"}}`, http.StatusTooManyRequests) + return + } + + next.ServeHTTP(w, r) + }) +} + +// UsageTracker middleware tracks API usage +func UsageTracker(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // TODO: Track usage in database + next.ServeHTTP(w, r) + }) +} + +// Logger middleware logs all HTTP requests +func Logger(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + // Create response wrapper to capture status code + wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK} + + next.ServeHTTP(wrapped, r) + + log.Printf( + "[%s] %s %s %d %v", + r.Method, + r.URL.Path, + r.RemoteAddr, + wrapped.statusCode, + time.Since(start), + ) + }) +} + +// responseWriter wraps http.ResponseWriter to capture status code +type responseWriter struct { + http.ResponseWriter + statusCode int +} + +func (rw *responseWriter) WriteHeader(code int) { + rw.statusCode = code + rw.ResponseWriter.WriteHeader(code) +} + +// RequestID middleware adds a unique request ID to each request +func RequestID(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + requestID := r.Header.Get("X-Request-ID") + if requestID == "" { + requestID = generateRequestID() + } + + w.Header().Set("X-Request-ID", requestID) + ctx := context.WithValue(r.Context(), RequestIDKey, requestID) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +// CORS middleware handles Cross-Origin Resource Sharing +func CORS(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + origin := r.Header.Get("Origin") + if origin != "" { + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, X-API-Key, X-Request-ID") + w.Header().Set("Access-Control-Max-Age", "86400") + } + + // Handle preflight requests + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusNoContent) + return + } + + next.ServeHTTP(w, r) + }) +} + +// Recovery middleware recovers from panics +func Recovery(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + defer func() { + if err := recover(); err != nil { + log.Printf("Panic recovered: %v", err) + http.Error(w, `{"success":false,"error":{"code":"INTERNAL_ERROR","message":"Internal server error"}}`, http.StatusInternalServerError) + } + }() + next.ServeHTTP(w, r) + }) +} + +// Secure middleware adds security headers +func Secure(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("X-XSS-Protection", "1; mode=block") + w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") + w.Header().Set("Content-Security-Policy", "default-src 'self'") + + next.ServeHTTP(w, r) + }) +} + +// generateRequestID creates a unique request identifier +func generateRequestID() string { + bytes := make([]byte, 16) + rand.Read(bytes) + return hex.EncodeToString(bytes) +} + +// Chain chains multiple middleware together +func Chain(middlewares ...func(http.Handler) http.Handler) func(http.Handler) http.Handler { + return func(final http.Handler) http.Handler { + for i := len(middlewares) - 1; i >= 0; i-- { + final = middlewares[i](final) + } + return final + } +}