diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..fb51acf3 --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +POSTGRES_USER="cairocoder" +POSTGRES_PASSWORD="cairocoder" +POSTGRES_DB="cairocoder" \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4adc4e5c..e38ec97e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: with: context: . push: true - file: ./ingest.dockerfile + file: ./ingester.dockerfile tags: ${{ steps.meta-ingester.outputs.tags }} labels: ${{ steps.meta-ingester.outputs.labels }} cache-from: type=gha diff --git a/.github/workflows/generate-embeddings.yml b/.github/workflows/generate-embeddings.yml index d6f66cd0..2fbf5e4e 100644 --- a/.github/workflows/generate-embeddings.yml +++ b/.github/workflows/generate-embeddings.yml @@ -87,7 +87,7 @@ jobs: PORT = 3001 SIMILARITY_MEASURE = "cosine" - [HOSTED_MODE] + [PROVIDERS] DEFAULT_CHAT_PROVIDER = "${{ secrets.DEFAULT_CHAT_PROVIDER }}" DEFAULT_CHAT_MODEL = "${{ secrets.DEFAULT_CHAT_MODEL }}" DEFAULT_FAST_CHAT_PROVIDER = "${{ secrets.DEFAULT_FAST_CHAT_PROVIDER }}" diff --git a/README.md b/README.md index f7f7132f..dca7e743 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,7 @@ - [Project Structure](#project-structure) - [RAG Pipeline](#rag-pipeline) - [Ingestion System](#ingestion-system) - - [Database](#database) - [Development](#development) -- [Upcoming Features](#upcoming-features) - [Contribution](#contribution) ## Credits @@ -34,7 +32,6 @@ Cairo Coder is an intelligent code generation service that makes writing Cairo s - **OpenAI Compatible API**: Interface compatible with the OpenAI API format for easy integration - **Multiple LLM Support**: Works with OpenAI, Anthropic, and Google models - **Source-Informed Generation**: Code is generated based on Cairo documentation, ensuring correctness -- **Streaming Response**: Support for response streaming for a responsive experience ## Installation @@ -64,12 +61,12 @@ There are mainly 2 ways of installing Cairo Coder - With Docker, Without Docker. 5. Inside the packages/agents package, copy the `sample.config.toml` file to a `config.toml`. For development setups, you need only fill in the following fields: - `OPENAI`: Your OpenAI API key. **You only need to fill this if you wish to use OpenAI's models**. - - `ANTHROPIC`: Your Anthropic API key. **You only need to fill this if you wish to use Anthropic models**. + - `GEMINI`: Your Gemini API key. **You only need to fill this if you wish to use Gemini models**. - `SIMILARITY_MEASURE`: The similarity measure to use (This is filled by default; you can leave it as is if you are unsure about it.) - - Models: The `[HOSTED_MODE]` table defines the underlying LLM model used. We recommend using: + - Models: The `[PROVIDERS]` table defines the underlying LLM model used. We recommend using: ```toml - [HOSTED_MODE] + [PROVIDERS] DEFAULT_CHAT_PROVIDER = "gemini" DEFAULT_CHAT_MODEL = "Gemini Flash 2.5" DEFAULT_FAST_CHAT_PROVIDER = "gemini" @@ -83,19 +80,15 @@ There are mainly 2 ways of installing Cairo Coder - With Docker, Without Docker. Cairo Coder uses PostgreSQL with pgvector for storing and retrieving vector embeddings. You need to configure both the database initialization and the application connection settings: **a. Database Container Initialization** (`.env` file): - Create a `.env` file in the root directory with the following PostgreSQL configuration: ``` POSTGRES_USER="YOUR_POSTGRES_USER" POSTGRES_PASSWORD="YOUR_POSTGRES_PASSWORD" - POSTGRES_ROOT_DB="YOUR_POSTGRES_ROOT_DB" - POSTGRES_HOST="localhost" - POSTGRES_PORT="5432" + POSTGRES_DB="YOUR_POSTGRES_DB" ``` This file is used by Docker to initialize the PostgreSQL container when it first starts. - The `POSTGRES_HOST` is set to "localhost" because this is from the database's own perspective. **b. Application Connection Settings** (`config.toml` file): @@ -105,16 +98,15 @@ There are mainly 2 ways of installing Cairo Coder - With Docker, Without Docker. [VECTOR_DB] POSTGRES_USER="YOUR_POSTGRES_USER" POSTGRES_PASSWORD="YOUR_POSTGRES_PASSWORD" - POSTGRES_ROOT_DB="YOUR_POSTGRES_ROOT_DB" + POSTGRES_DB="YOUR_POSTGRES_DB" POSTGRES_HOST="postgres" POSTGRES_PORT="5432" ``` This configuration is used by the backend and ingester services to connect to the database. - Note that `POSTGRES_HOST` is set to "postgres", which is the service name in docker-compose.yml. + Note that `POSTGRES_HOST` is set to ```"postgres"``` and `POSTGRES_PORT` to ```"5432"```, which are the container's name and port in docker-compose.yml. - **Important:** Make sure to use the same password in both files. The first file initializes the - database, while the second is used by your application to connect to it. + **Important:** Make sure to use the same password, username and db's name in both files. The first file initializes the database, while the second is used by your application to connect to it. 7. **Configure LangSmith (Optional)** @@ -131,6 +123,7 @@ There are mainly 2 ways of installing Cairo Coder - With Docker, Without Docker. LANGSMITH_API_KEY="" LANGCHAIN_PROJECT="" ``` + - Add the `.env` in an env_file section in the backend service of the docker-compose.yml With this configuration, all LLM calls and chain executions will be logged to your LangSmith project, allowing you to debug, analyze, and improve the system's performance. @@ -138,17 +131,17 @@ There are mainly 2 ways of installing Cairo Coder - With Docker, Without Docker. 9. Run the application using one of the following methods: ```bash - docker-compose up --build + docker compose up postgres backend ``` -8. The API will be available at http://localhost:3000/chat/completions. +8. The API will be available at http://localhost:3001/v1/chat/completions ## Running the Ingester After you have the main application running, you might need to run the ingester to process and embed documentation from various sources. The ingester is configured as a separate profile in the docker-compose file and can be executed as follows: ```bash - docker-compose --profile ingester up ingester + docker compose up ingester ``` Once the ingester completes its task, the vector database will be populated with embeddings from all the supported documentation sources, making them available for RAG-based code generation requests to the API. @@ -160,46 +153,71 @@ Cairo Coder provides a simple REST API compatible with the OpenAI format for eas ### Endpoint ``` -POST /chat/completions +POST /v1/chat/completions ``` ### Request Format -```json -{ - "model": "gemini-2.0-flash", - "messages": [ - { - "role": "system", - "content": "You are a Cairo programming expert." - }, - { - "role": "user", - "content": "Write a Cairo contract that implements a simple ERC-20 token." - } - ], - "temperature": 0.7, -} +Example of a simple request: + +```bash +curl -X POST http://localhost:3001/v1/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gemini-2.5-flash", + "messages": [ + { + "role": "user", + "content": "How do I implement storage in Cairo?" + } + ] + }' ``` +The API accepts all standard OpenAI Chat Completions parameters. + +**Supported Parameters:** +- `model`: Model identifier (string) +- `messages`: Array of message objects with `role` and `content` +- `temperature`: Controls randomness (0-2, default: 0.7) +- `top_p`: Nucleus sampling parameter (0-1, default: 1) +- `n`: Number of completions (default: 1) +- `stream`: Enable streaming responses (boolean, default: false) +- `max_tokens`: Maximum tokens in response +- `stop`: Stop sequences (string or array) +- `presence_penalty`: Penalty for token presence (-2 to 2) +- `frequency_penalty`: Penalty for token frequency (-2 to 2) +- `logit_bias`: Token bias adjustments +- `user`: User identifier +- `response_format`: Response format specification + + ### Response Format +#### Standard Mode Response + ```json { - "id": "gen-123456", + "id": "chatcmpl-123456", "object": "chat.completion", "created": 1717273561, - "model": "gemini-2.0-flash", + "model": "gemini-2.5-flash", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "#[starknet::contract]\nmod ERC20 {\n // Contract code here...\n}" + "content": "#[starknet::contract]\nmod ERC20 {\n use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};\n \n #[storage]\n struct Storage {\n name: felt252,\n symbol: felt252,\n total_supply: u256,\n balances: Map,\n }\n // ... contract implementation\n}" }, + "logprobs": null, "finish_reason": "stop" } - ] + ], + "usage": { + "prompt_tokens": 45, + "completion_tokens": 120, + "total_tokens": 165 + } } ``` @@ -245,13 +263,6 @@ Currently supported documentation sources include: - Cairo Foundry documentation - Cairo By Examples -### Database - -Cairo Coder uses MongoDB Atlas with vector search capabilities for similarity search: - -- **Vector Database**: Stores document embeddings for efficient similarity search -- **Vector Search**: Uses cosine similarity to find relevant Cairo documentation - ## Development For development, you can use the following commands: diff --git a/docker-compose.yml b/docker-compose.yml index 46612451..421f5dcd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,12 @@ -version: '3.8' - services: postgres: image: pgvector/pgvector:pg17 - container_name: "postgresql" + container_name: 'postgres' shm_size: 1g env_file: - .env ports: - - 5432:5432 + - 5455:5432 volumes: - ./data:/var/lib/postgresql/data restart: unless-stopped @@ -19,12 +17,9 @@ services: build: context: . dockerfile: backend.dockerfile + container_name: 'cairo-coder-backend' ports: - 3001:3001 - extra_hosts: - - host.docker.internal:host-gateway - env_file: - - packages/backend/.env depends_on: postgres: condition: service_started @@ -34,10 +29,10 @@ services: ingester: platform: linux/amd64 + container_name: 'cairo-coder-ingester' build: context: . - dockerfile: ingest.dockerfile - profiles: ["ingester"] + dockerfile: ingester.dockerfile depends_on: postgres: condition: service_started diff --git a/ingest.dockerfile b/ingester.dockerfile similarity index 100% rename from ingest.dockerfile rename to ingester.dockerfile diff --git a/packages/agents/.env.test b/packages/agents/.env.test deleted file mode 100644 index 1ff6fc42..00000000 --- a/packages/agents/.env.test +++ /dev/null @@ -1,2 +0,0 @@ -API_KEY="test" -API_URL="http://localhost:3005" \ No newline at end of file diff --git a/packages/agents/sample.config.toml b/packages/agents/sample.config.toml index 79bad0a6..5cd9fabf 100644 --- a/packages/agents/sample.config.toml +++ b/packages/agents/sample.config.toml @@ -5,21 +5,18 @@ ANTHROPIC = "" DEEPSEEK = "" GEMINI = "" -[API_ENDPOINTS] -OLLAMA = "" - [VECTOR_DB] POSTGRES_USER="cairocoder" POSTGRES_PASSWORD="cairocoder" -POSTGRES_ROOT_DB="cairocoder" -POSTGRES_HOST="localhost" +POSTGRES_DB="cairocoder" +POSTGRES_HOST="postgres" POSTGRES_PORT="5432" [GENERAL] PORT = 3_001 SIMILARITY_MEASURE = "cosine" -[HOSTED_MODE] +[PROVIDERS] DEFAULT_CHAT_PROVIDER = "gemini" DEFAULT_CHAT_MODEL = "Gemini Flash 2.5" DEFAULT_FAST_CHAT_PROVIDER = "gemini" diff --git a/packages/agents/src/config/settings.ts b/packages/agents/src/config/settings.ts index 20870513..166ba028 100644 --- a/packages/agents/src/config/settings.ts +++ b/packages/agents/src/config/settings.ts @@ -26,7 +26,7 @@ const loadConfig = () => { ) as any as Config; }; -export const getHostedModeConfig = () => loadConfig().HOSTED_MODE; +export const getHostedModeConfig = () => loadConfig().PROVIDERS; export const getPort = () => loadConfig().GENERAL.PORT; @@ -49,7 +49,7 @@ export const getVectorDbConfig = () => { return { POSTGRES_USER: config.VECTOR_DB.POSTGRES_USER || '', POSTGRES_PASSWORD: config.VECTOR_DB.POSTGRES_PASSWORD || '', - POSTGRES_ROOT_DB: config.VECTOR_DB.POSTGRES_ROOT_DB || '', + POSTGRES_DB: config.VECTOR_DB.POSTGRES_DB || '', POSTGRES_HOST: config.VECTOR_DB.POSTGRES_HOST || '', POSTGRES_PORT: config.VECTOR_DB.POSTGRES_PORT || '', } as VectorStoreConfig; diff --git a/packages/agents/src/db/postgresVectorStore.ts b/packages/agents/src/db/postgresVectorStore.ts index beef5d38..f61f385c 100644 --- a/packages/agents/src/db/postgresVectorStore.ts +++ b/packages/agents/src/db/postgresVectorStore.ts @@ -108,7 +108,7 @@ export class VectorStore { const pool = new Pool({ user: config.POSTGRES_USER, host: config.POSTGRES_HOST, - database: config.POSTGRES_ROOT_DB, + database: config.POSTGRES_DB, password: config.POSTGRES_PASSWORD, port: parseInt(config.POSTGRES_PORT), max: 10, diff --git a/packages/agents/src/types/index.ts b/packages/agents/src/types/index.ts index 4c875d3d..7e281d1b 100644 --- a/packages/agents/src/types/index.ts +++ b/packages/agents/src/types/index.ts @@ -13,7 +13,7 @@ export interface LLMConfig { export interface VectorStoreConfig { POSTGRES_USER: string; POSTGRES_PASSWORD: string; - POSTGRES_ROOT_DB: string; + POSTGRES_DB: string; POSTGRES_HOST: string; POSTGRES_PORT: string; } @@ -31,7 +31,7 @@ export interface Config { DEEPSEEK: string; GEMINI: string; }; - HOSTED_MODE?: { + PROVIDERS?: { DEFAULT_CHAT_PROVIDER: string; DEFAULT_CHAT_MODEL: string; DEFAULT_FAST_CHAT_PROVIDER: string; diff --git a/packages/backend/src/config/provider/index.ts b/packages/backend/src/config/provider/index.ts index a454b9e8..cdf5b529 100644 --- a/packages/backend/src/config/provider/index.ts +++ b/packages/backend/src/config/provider/index.ts @@ -55,13 +55,13 @@ export const getAvailableEmbeddingModelProviders = async () => { } const hostedModeConfig = getHostedModeConfig(); - const hosted_model = + const PROVIDERSl = models[hostedModeConfig.DEFAULT_EMBEDDING_PROVIDER][ hostedModeConfig.DEFAULT_EMBEDDING_MODEL ]; return { [hostedModeConfig.DEFAULT_EMBEDDING_PROVIDER]: { - [hostedModeConfig.DEFAULT_EMBEDDING_MODEL]: hosted_model, + [hostedModeConfig.DEFAULT_EMBEDDING_MODEL]: PROVIDERSl, }, }; };