diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 9da7f0a..ca13c68 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md -Этот файл содержит контекст для Claude Code при работе с проектом. +Контекст для Claude Code при работе с проектом. ## О проекте @@ -36,22 +36,35 @@ app/ │ ├── config.py # Pydantic Settings │ ├── db.py # SQLModel database │ ├── health.py # Health endpoint -│ └── llm_client.py # OpenRouter client -├── models/ # SQLModel entities -├── schemas/ # API request/response -├── services/ # ChatService -├── integrations/ # Notion, BehaviorManager -├── behavior/ # Behavior models -└── observability/ # OpenTelemetry tracing +│ ├── llm_client.py # OpenRouter client +│ └── project_memory.py # In-memory fallback +├── models/ +│ ├── chat.py # Chat models +│ └── db_models.py # SQLModel entities +├── schemas/ +│ ├── chat.py # Chat request/response +│ └── projects.py # Project schemas +├── services/ +│ └── chat_service.py # ChatService бизнес-логика +├── integrations/ +│ ├── notion_client.py # Notion API client +│ └── behavior_manager.py # Behavior loading +├── behavior/ +│ └── models.py # Behavior models +└── observability/ + └── tracing.py # OpenTelemetry setup ``` -## Ключевые файлы +## API Endpoints -- `app/main.py` — точка входа, lifespan context manager -- `app/api.py` — все API endpoints -- `app/core/config.py` — Settings с SettingsConfigDict -- `app/core/llm_client.py` — OpenRouterClient -- `app/services/chat_service.py` — бизнес-логика чатов +- `GET /` — status +- `GET /health` — health check +- `POST /api/v1/chat` — standalone chat +- `POST /api/v1/projects` — create project +- `GET /api/v1/projects` — list projects +- `POST /api/v1/projects/{id}/chat` — chat in project +- `GET /api/v1/projects/{id}/history` — chat history +- `GET /api/v1/behavior/schema` — current behavior ## Стек @@ -61,6 +74,9 @@ app/ - OpenTelemetry (traces, logs, metrics) - OpenRouter (LLM) - uv (package manager) +- structlog (logging) +- httpx (HTTP client) +- notion-client (Notion API) ## Конвенции @@ -68,8 +84,10 @@ app/ - Datetime: `datetime.now(UTC)` вместо `datetime.utcnow()` - FastAPI: lifespan context manager вместо `@app.on_event` - Тесты: `OTEL_SDK_DISABLED=true` для отключения трейсинга +- Логирование: `enrich_context(event="name").info("message")` ## Связанные репозитории -- `app-crewai-cluster` — CrewAI агенты (отдельный сервис) -- `app-release` — Helm charts и GitOps +- `app-poly-gitops-k8s` — GitOps манифесты (ArgoCD Applications) +- `app-poly-gitops-helm` — Helm chart для сервисов +- `app-poly-gitops-crewai` — CrewAI мониторинг diff --git a/.env.example b/.env.example index 5514d03..1276ae4 100644 --- a/.env.example +++ b/.env.example @@ -1,20 +1,13 @@ # ============================================================================= -# Chat API Configuration +# Chat API Configuration (synced with Helm values) # ============================================================================= # ----------------------------------------------------------------------------- -# LLM Provider (OpenRouter) +# OpenRouter LLM # ----------------------------------------------------------------------------- OPENROUTER_API_KEY=sk-or-v1-... -OPENROUTER_MODEL=anthropic/claude-opus-4 OPENROUTER_API_URL=https://openrouter.ai/api/v1/chat/completions -OPENROUTER_HTTP_REFERER=https://your-app.com -OPENROUTER_X_TITLE=Chat API - -# Legacy (if using LiteLLM proxy or OpenAI directly) -OPENAI_API_KEY= -LLM_API_URL=http://localhost:4000 -CHAT_MODEL=openai/gpt-4.1 +OPENROUTER_MODEL=anthropic/claude-sonnet-4 # ----------------------------------------------------------------------------- # Database @@ -22,35 +15,23 @@ CHAT_MODEL=openai/gpt-4.1 # SQLite (default for local dev) DATABASE_URL=sqlite:///db.sqlite3 -# PostgreSQL (docker-compose) -POSTGRES_USER=chatuser -POSTGRES_PASSWORD=changeme -POSTGRES_DB=chatdb -# DATABASE_URL=postgresql://chatuser:changeme@localhost:5432/chatdb - -# ----------------------------------------------------------------------------- -# Notion Integration (optional) -# ----------------------------------------------------------------------------- -NOTION_TOKEN= -NOTION_PAGE_ID= +# PostgreSQL (production via PGO secret) +# DATABASE_URL=postgresql://user:pass@host:5432/chatdb # ----------------------------------------------------------------------------- -# OpenTelemetry (observability) +# OpenTelemetry # ----------------------------------------------------------------------------- OTEL_SERVICE_NAME=chat-api OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -ENVIRONMENT=development - -# Kubernetes metadata (auto-populated in K8s) -K8S_POD_NAME= -K8S_NAMESPACE= -K8S_NODE_NAME= -K8S_DEPLOYMENT_NAME= +OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf +OTEL_TRACES_EXPORTER=otlp +OTEL_LOGS_EXPORTER=otlp -# Disable OTEL for local dev/testing +# Disable for local dev/testing # OTEL_SDK_DISABLED=true # ----------------------------------------------------------------------------- # Application # ----------------------------------------------------------------------------- -PROJECT_NAME=ChatMicroservice +ENVIRONMENT=development +LOG_LEVEL=INFO diff --git a/.gitignore b/.gitignore index 41ed87c..12a441b 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ build/ .coverage htmlcov/ *.sqlite3 +node_modules/ diff --git a/.releaserc.json b/.releaserc.json index e253ed2..3467942 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -4,20 +4,77 @@ [ "@semantic-release/commit-analyzer", { + "preset": "conventionalcommits", "releaseRules": [ {"type": "feat", "release": "minor"}, {"type": "fix", "release": "patch"}, {"type": "perf", "release": "patch"}, {"type": "revert", "release": "patch"}, {"type": "docs", "release": "patch"}, + {"type": "style", "release": "patch"}, {"type": "refactor", "release": "patch"}, + {"type": "test", "release": "patch"}, {"type": "ci", "release": "patch"}, {"type": "chore", "release": "patch"}, - {"breaking": true, "release": "major"} - ] + {"type": "build", "release": "patch"}, + {"breaking": true, "release": "major"}, + {"scope": "no-release", "release": false} + ], + "parserOpts": { + "noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES", "BREAKING"] + } } ], - "@semantic-release/release-notes-generator", - "@semantic-release/github" + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + {"type": "feat", "section": "🚀 Features"}, + {"type": "fix", "section": "🐛 Bug Fixes"}, + {"type": "perf", "section": "⚡ Performance Improvements"}, + {"type": "revert", "section": "⏪ Reverts"}, + {"type": "docs", "section": "📚 Documentation"}, + {"type": "style", "section": "💄 Styles"}, + {"type": "refactor", "section": "♻️ Code Refactoring"}, + {"type": "test", "section": "✅ Tests"}, + {"type": "ci", "section": "🔧 CI/CD"}, + {"type": "chore", "section": "🏗️ Chores"}, + {"type": "build", "section": "📦 Build System"} + ] + }, + "writerOpts": { + "commitsSort": ["subject", "scope"] + } + } + ], + [ + "@semantic-release/changelog", + { + "changelogFile": "CHANGELOG.md" + } + ], + [ + "@semantic-release/github", + { + "assets": [ + { + "path": "CHANGELOG.md", + "label": "Changelog" + } + ], + "successComment": false, + "failComment": false, + "releasedLabels": false + } + ], + [ + "@semantic-release/git", + { + "assets": ["CHANGELOG.md"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ] ] } diff --git a/app/core/config.py b/app/core/config.py index 11b8361..5290fb8 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -1,4 +1,3 @@ -import os from functools import lru_cache from pydantic_settings import BaseSettings, SettingsConfigDict @@ -8,16 +7,19 @@ class Settings(BaseSettings): """ - Конфиг всего приложения. - Все переменные тянутся из .env — легко расширять и объяснять новым людям. + Конфиг приложения. Все переменные из ENV (синхронизировано с Helm values). """ - openai_api_key: str = os.getenv("OPENAI_API_KEY", "") - llm_api_url: str = "http://localhost:4000" - chat_model: str = "openai/gpt-4.1" - project_name: str = "ChatMicroservice" - notion_token: str = os.getenv("NOTION_TOKEN", "") - notion_page_id: str = os.getenv("NOTION_PAGE_ID", "") - database_url: str = os.getenv("DATABASE_URL", "sqlite:///db.sqlite3") + # OpenRouter LLM + openrouter_api_key: str = "" + openrouter_api_url: str = "https://openrouter.ai/api/v1/chat/completions" + openrouter_model: str = "anthropic/claude-sonnet-4" + + # Database + database_url: str = "sqlite:///db.sqlite3" + + # Application + environment: str = "development" + log_level: str = "INFO" model_config = SettingsConfigDict( env_file=".env", diff --git a/app/main.py b/app/main.py index 6fa1521..2bfa974 100644 --- a/app/main.py +++ b/app/main.py @@ -5,11 +5,8 @@ from fastapi.middleware.cors import CORSMiddleware from .api import api_router -from .core.config import get_settings from .core.db import init_db from .core.health import health_router -from .integrations.behavior_manager import BehaviorManager -from .integrations.notion_client import NotionClient from .logger import enrich_context from .observability.tracing import setup_tracing @@ -17,8 +14,6 @@ @asynccontextmanager async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: """Lifespan event handler for startup and shutdown.""" - settings = get_settings() - # Startup: init database enrich_context(event="startup_init_db_start").info("Starting database initialization") try: @@ -28,25 +23,6 @@ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]: enrich_context(event="startup_init_db_error", error=str(e)).error("Database initialization failed") raise - # Startup: load behavior from Notion (if configured) - if settings.notion_token and settings.notion_page_id: - enrich_context(event="notion_config_found").info("Notion configuration found") - notion_client = NotionClient(settings.notion_token) - behavior_manager = BehaviorManager(notion_client, settings.notion_page_id) - app.state.behavior_manager = behavior_manager - - enrich_context(event="startup_behavior_start").info("Starting behavior loading") - try: - await behavior_manager.refresh() - enrich_context(event="startup_behavior_success").info("Behavior loading completed") - except Exception as e: - enrich_context(event="startup_behavior_error", error=str(e)).error("Behavior loading failed") - # Не падаем на этой ошибке - else: - enrich_context(event="notion_config_missing").info( - "Notion configuration not found, skipping behavior loading" - ) - enrich_context(event="startup").info("Application initialized") yield # Application runs here diff --git a/tests/test_config.py b/tests/test_config.py index 7c0409d..091a866 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -6,9 +6,9 @@ def test_settings_defaults(): """Test Settings has correct defaults.""" settings = Settings() - assert settings.llm_api_url == "http://localhost:4000" - assert settings.chat_model == "openai/gpt-4.1" - assert settings.project_name == "ChatMicroservice" + assert settings.openrouter_api_url == "https://openrouter.ai/api/v1/chat/completions" + assert settings.openrouter_model == "anthropic/claude-sonnet-4" + assert settings.environment == "development" def test_get_settings_cached():