Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,27 @@ pnpm --dir bridge install
pnpm tauri dev
```

To build the bridge package for your platform, run one of these from the repo root:

```bash
cd bridge
pnpm build:pkg:win
```

or on Linux:

```bash
cd bridge
pnpm build:pkg:linux
```

If you are working on bridge-related code, you can run bridge tests directly:

```bash
cd bridge

docker compose -f docker-compose.test.yml up -d

pnpm test
```

Expand Down
24 changes: 24 additions & 0 deletions FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,30 @@ RelWave includes native Git integration powered by `simple-git`, providing a ful

---

## AI Features and Providers

RelWave integrates deeply with multiple Large Language Models (LLMs) to provide intelligent database assistance, schema analysis, and query explanations.

### Supported Providers
The application supports a flexible, multi-provider AI architecture:
- **OpenAI** (GPT-4o, GPT-4 Turbo, etc.)
- **Anthropic** (Claude 3.5 Sonnet, Opus, etc.)
- **Mistral** (Mistral Large, Mistral Small, etc.)
- **Groq** (Llama 3, Mixtral, etc. for ultra-fast inference)
- **Ollama** (Local models like Llama 3, Phi-3, Mistral)
- **Google Gemini** (Gemini 1.5 Pro, Flash)

### AI Capabilities
| Feature | Description |
| ------- | ----------- |
| **Schema Analysis** | Analyzes the structure of your database, identifying relationships, potential optimizations, and providing a human-readable summary of complex schemas. Uses a highly optimized, token-efficient dense schema representation. |
| **Query Explanation** | Breaks down complex SQL queries into plain English, explaining joins, filters, performance implications, and the overall intent of the query. |
| **Local AI Support** | Full privacy and zero-cost inference available through local Ollama integration, ensuring sensitive database schemas never leave your machine. |
| **Context Aware** | The AI system automatically receives the dialect (PostgreSQL, MySQL, SQLite) and the relevant database schema context to provide accurate, dialect-specific responses. |
| **High Token Capacity** | Configured with large output windows (up to 4096 tokens) to handle extensive schema analyses and complex query breakdowns without truncation. |

---

## Visual Tools

### Chart Visualization
Expand Down
12 changes: 9 additions & 3 deletions bridge/__tests__/projectStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,12 @@ describe("ProjectStore", () => {
name: "users",
type: "BASE TABLE",
columns: [
{ name: "id", type: "integer", nullable: false, isPrimaryKey: true, isForeignKey: false, defaultValue: null, isUnique: true },
{ name: "email", type: "varchar(255)", nullable: false, isPrimaryKey: false, isForeignKey: false, defaultValue: null, isUnique: true },
{ name: "id", type: "integer", nullable: false, isPrimaryKey: true, isForeignKey: false, defaultValue: null, isUnique: true, isSerial: true, ordinalPosition: 1 },
{ name: "email", type: "varchar(255)", nullable: false, isPrimaryKey: false, isForeignKey: false, defaultValue: null, isUnique: true, isSerial: false, ordinalPosition: 2 },
],
indexes: [],
foreignKeys: [],
checks: [],
},
],
},
Expand Down Expand Up @@ -301,8 +304,11 @@ describe("ProjectStore", () => {
name: "posts",
type: "BASE TABLE",
columns: [
{ name: "id", type: "integer", nullable: false, isPrimaryKey: true, isForeignKey: false, defaultValue: null, isUnique: true },
{ name: "id", type: "integer", nullable: false, isPrimaryKey: true, isForeignKey: false, defaultValue: null, isUnique: true, isSerial: true, ordinalPosition: 1 },
],
indexes: [],
foreignKeys: [],
checks: [],
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"pg-query-stream": "^4.10.3",
"pino": "^9.14.0",
"ssh2": "^1.17.0",
"uuid": "^8.3.2",
"uuid": "^9.0.1",
"ws": "^8.19.0"
Comment thread
Yashh56 marked this conversation as resolved.
},
"bin": "./dist/index.cjs",
Expand Down
10 changes: 5 additions & 5 deletions bridge/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 12 additions & 15 deletions bridge/src/ai/prompts/schema-analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,22 @@ export function buildSchemaAnalysisPrompt(input: SchemaAnalysisInput): {
if (c.isPrimaryKey) flags.push("PK");
if (c.isForeignKey) flags.push("FK");
if (!c.nullable) flags.push("NOT NULL");
if (c.references) flags.push(`→ ${c.references.table}.${c.references.column}`);
return ` - ${c.name} (${c.type})${flags.length ? " [" + flags.join(", ") + "]" : ""}`;
if (c.references) flags.push(`→${c.references.table}.${c.references.column}`);
return `${c.name}(${c.type})${flags.length ? "[" + flags.join(",") + "]" : ""}`;
})
.join("\n");
.join(", ");

const extras: string[] = [];
if (t.indexes?.length) extras.push(`Indexes: ${t.indexes.join(", ")}`);
if (t.foreignKeys?.length) extras.push(`Foreign keys: ${t.foreignKeys.join(", ")}`);
if (t.constraints?.length) extras.push(`Constraints: ${t.constraints.join(", ")}`);

return [
`### Table: ${t.schema ? `${t.schema}.` : ""}${t.name}`,
columns,
extras.length ? extras.map((e) => ` * ${e}`).join("\n") : "",
]
.filter(Boolean)
.join("\n");
if (t.indexes?.length) extras.push(`idx: ${t.indexes.join(",")}`);
if (t.foreignKeys?.length) extras.push(`fk: ${t.foreignKeys.join(",")}`);
if (t.constraints?.length) extras.push(`cons: ${t.constraints.join(",")}`);

const tableName = `${t.schema ? `${t.schema}.` : ""}${t.name}`;
const extrasStr = extras.length ? ` | ${extras.join(" | ")}` : "";

return `[${tableName}] cols: ${columns}${extrasStr}`;
})
.join("\n\n");
.join("\n");

const dbType = input.databaseType ? ` (${input.databaseType})` : "";

Expand Down
2 changes: 1 addition & 1 deletion bridge/src/ai/providers/anthropic.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class AnthropicProvider implements AIProvider {
try {
const msg = await this.client.messages.create({
model: DEFAULT_MODEL,
max_tokens: 2048,
max_tokens: 4096,
system,
messages: [{ role: "user", content: user }],
});
Expand Down
1 change: 1 addition & 0 deletions bridge/src/ai/providers/gemini.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class GeminiProvider implements AIProvider {
const model = this.genAI.getGenerativeModel({
model: DEFAULT_MODEL,
systemInstruction: system,
generationConfig: { maxOutputTokens: 4096 },
});
const result = await model.generateContent(user);
return result.response.text();
Expand Down
2 changes: 1 addition & 1 deletion bridge/src/ai/providers/groq.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class GroqProvider implements AIProvider {
{ role: "system", content: system },
{ role: "user", content: user },
],
max_tokens: 2048,
max_tokens: 4096,
});
return res.choices[0]?.message?.content ?? "";
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion bridge/src/ai/providers/mistral.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class MistralProvider implements AIProvider {
{ role: "system", content: system },
{ role: "user", content: user },
],
maxTokens: 2048,
maxTokens: 4096,
});
const choice = res.choices?.[0];
const content = choice?.message?.content;
Expand Down
1 change: 1 addition & 0 deletions bridge/src/ai/providers/ollama.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class OllamaProvider implements AIProvider {
{ role: "system", content: system },
{ role: "user", content: user },
],
options: { num_predict: 4096 },
});
return res.message?.content ?? "";
} catch (err) {
Expand Down
2 changes: 1 addition & 1 deletion bridge/src/ai/providers/openai.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export class OpenAIProvider implements AIProvider {
{ role: "system", content: system },
{ role: "user", content: user },
],
max_tokens: 2048,
max_tokens: 4096,
});
return res.choices[0]?.message?.content ?? "";
} catch (err) {
Expand Down
26 changes: 26 additions & 0 deletions bridge/src/connectors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Database Connectors (Bridge)

This folder contains database-specific connector implementations used by the bridge to execute metadata, query, migration and CRUD operations.

Layout
- `postgres.ts` - PostgreSQL connector, cache manager, metadata helpers, streaming query support and PostgreSQL-specific DDL/DML behavior.
- `mysql.ts` - MySQL connector for schema/table metadata, stats, migrations and query execution.
- `mariadb.ts` - MariaDB connector. Keep MariaDB behavior separate when it diverges from MySQL.
- `sqlite.ts` - SQLite connector using `better-sqlite3`, local file handling and SQLite-specific migration/query behavior.

How it fits
- `src/services/queryExecutor.ts` routes bridge operations to the correct connector based on `DBType`.
- SQL strings should come from `src/queries/*` where practical, not be duplicated inline.
- Shared result shapes come from `src/types/common.ts`; database-specific config and metadata types live in `src/types/postgres.ts`, `src/types/mysql.ts` and `src/types/sqlite.ts`.
- Connection config objects are built by `src/services/connectionBuilder.ts`; connectors generally create their own driver client from that config for each operation.

How to add connector behavior
1. Add database-specific SQL to `src/queries/<db>/` when the operation needs raw SQL.
2. Add or update typed method support in the relevant connector file.
3. Route the operation from `QueryExecutor` or the owning service.
4. Register any new public RPC method through a handler in `src/handlers`.

Notes
- Keep connector methods focused on database interaction. Do not read UI payloads directly or handle RPC responses here.
- Invalidate or bypass connector caches when an operation mutates schema, table data or migration state.
- Prefer shared common types unless a database really needs extra metadata fields.
43 changes: 35 additions & 8 deletions bridge/src/connectors/mariadb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import mysql, {
import { loadLocalMigrations, writeBaselineMigration } from "../utils/baselineMigration";
import crypto from "crypto";
import fs from "fs";
import { ensureDir, getMigrationsDir } from "../utils/config";
import { ensureDir } from "../utils/config";
import { projectStoreInstance } from "../services/projectStore";
import {
CacheEntry,
CACHE_TTL,
Expand All @@ -34,6 +35,7 @@ import {
MySQLAlterTableOperation as MariaDBAlterTableOperation,
MySQLDropMode as MariaDBDropMode,
} from "../types/mysql";
import { SchemaFile } from "../services/projectStore";

export type {
ColumnDetail,
Expand Down Expand Up @@ -548,7 +550,7 @@ export function streamQueryCancelable(
conn = await pool.getConnection();

const [pidRows] = await conn.execute(GET_CONNECTION_ID);
backendPid = pidRows[0].pid;
backendPid = (pidRows as any[])[0].pid;

const raw = (conn as any).connection;
query = raw.query(sql);
Expand Down Expand Up @@ -916,7 +918,12 @@ export async function getSchemaMetadataBatch(
not_nullable: Boolean(row.not_nullable),
default_value: row.default_value,
is_primary_key: Boolean(row.is_primary_key),
is_foreign_key: Boolean(row.is_foreign_key)
is_foreign_key: Boolean(row.is_foreign_key),
is_unique: Boolean(row.is_unique),
is_serial: Boolean(row.is_serial),
comment: row.comment,
check_constraint: row.check_constraint,
ordinal_position: row.ordinal_position
});
}

Expand Down Expand Up @@ -1349,10 +1356,10 @@ export async function insertBaseline(
await connection.query(INSERT_MIGRATION, [version, name, checksum]);
}


export async function baselineIfNeeded(
conn: MariaDBConfig,
migrationsDir: string
migrationsDir: string,
snapshot?: SchemaFile
) {
try {
await ensureMigrationTable(conn);
Expand All @@ -1363,10 +1370,22 @@ export async function baselineIfNeeded(
const version = Date.now().toString();
const name = "baseline_existing_schema";

const fakeSnapshot = snapshot || {
version: 2,
projectId: "",
databaseId: "",
dialect: "mysql",
schemas: [],
cachedAt: "",
relwaveVersion: "",
schemaHash: ""
};

const filePath = writeBaselineMigration(
migrationsDir,
version,
name
name,
fakeSnapshot
);

const checksum = crypto
Expand Down Expand Up @@ -1420,11 +1439,19 @@ export async function connectToDatabase(
options?: { readOnly?: boolean }
) {
let baselineResult = { baselined: false };
const migrationsDir = getMigrationsDir(connectionId);
const migrationsDir = await projectStoreInstance.resolveMigrationsDir(connectionId);
ensureDir(migrationsDir);
// 1️⃣ Baseline (ONLY if not read-only)
if (!options?.readOnly) {
baselineResult = await baselineIfNeeded(cfg, migrationsDir);
// Pass real schema snapshot so baseline contains actual DDL
let snapshot: any = undefined;
try {
const project = await projectStoreInstance.getProjectByDatabaseId(connectionId);
if (project) {
snapshot = await projectStoreInstance.getSchema(project.id) || undefined;
}
} catch { }
baselineResult = await baselineIfNeeded(cfg, migrationsDir, snapshot);
}

// 2️⃣ Load schema (read-only introspection)
Expand Down
Loading
Loading