Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 DEVELOPERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Set up for local development

1. `pnpm i`
2. `pnpm run build`
3. `pnpm run bundle`

When developing locally, configure your provider in the client (e.g., VS Code) by using the path to the bundled .js file:

```json
"openctx.providers": {
"file:///<path/to/js/bundle>/bundle.js": true,
}
```

## Debug

To see console log statements of your provider, open the vscode developer tools: [`cmd + Shift + P`] > "Toggle Developer Tools".
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is probably okay, but just wondering if there's a better way to show debug info, maybe in Cody's output channel. @keegancsmith do you know if it's possible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is not currently, but that would be a good improvement for us to add.

17 changes: 17 additions & 0 deletions pnpm-lock.yaml

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

22 changes: 22 additions & 0 deletions provider/postgres/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Postgres context provider for OpenCtx

This is a context provider for [OpenCtx](https://openctx.org) that brings context from your Postgres DB to code AI and editors. Postgres context provider allows to add details about a specific schema from your Postgres Database.

**Status:** Experimental

## Configuration

```json
"openctx.providers": {
// ...other providers...
"https://openctx.org/npm/@openctx/provider-postgres": {
"DB_URL": "<Database URL>"
}
},
```

## Development

- [Source code](https://sourcegraph.com/github.com/sourcegraph/openctx/-/tree/provider/postgres)
- [Docs](https://openctx.org/docs/providers/postgres)
- License: Apache 2.0
84 changes: 84 additions & 0 deletions provider/postgres/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { ItemsResult } from '@openctx/provider';
import dedent from 'dedent';
import postgres from 'postgres';

export class PostgresClient {
private readonly sql: postgres.Sql;
public schemas: string[] = [];

constructor(DB_URL: string) {
this.sql = postgres(DB_URL);
}

public getSchemas(): string[] {
return this.schemas;
}

public async getSchema(schema: string): Promise<ItemsResult> {
const res = await this.sql<
{ ['table_name']: string; [key: string]: any }[]
>`
SELECT table_name, column_name, data_type, character_maximum_length, column_default, is_nullable
FROM information_schema.columns
where table_schema = '${this.sql.unsafe(schema)}'`;

const schemaDDL = this.schemaToDDL(res);

const schemaPrompt = dedent`
The reference database schema for this question is ${schemaDDL}.
IMPORTANT: Be sure you only use the tables and columns from this schema in your answer.
`;

return [
{
title: schema,
ai: { content: schemaPrompt },
},
];
}

// ----------- Helper function ---------------

private schemaToDDL(
schema: { ['table_name']: string; [key: string]: any }[]
) {
const tables: { [key: string]: any } = {};
for (let row of schema) {
tables[row.table_name] = row;
}
const out = [];
const tableNames = Object.keys(tables);
for (let table of tableNames) {
const sql = [`create table ${table}(\n`];
const cols = schema.filter((s) => s.table_name === table);
for (let c of cols) {
let colSql = '';
//if (c.column_name === null || c.column_name === "") continue;
colSql = ` ${c.column_name} ${c.data_type}`;
if (c.is_nullable === 'NO') colSql += ' not null ';
if (c.column_default === 'NO')
colSql += ` default ${c.column_default} `;
colSql += ',\n';
sql.push(colSql);
}
sql.push(');');
out.push(sql.join(''));
}
return out.join('\n');
}

// ----------- Initialization function ---------------

public async initializePGData() {
await this.initializeSchemas();
}

private async initializeSchemas() {
const schemas = await this.sql`
select schema_name
from information_schema.schemata;
`;
console.log({ schemas });
this.schemas = schemas.map((schema) => schema.schema_name);
}
}
77 changes: 77 additions & 0 deletions provider/postgres/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type {
ItemsParams,
ItemsResult,
MentionsParams,
MentionsResult,
MetaResult,
} from '@openctx/provider';
import { PostgresClient } from './client.js';

/** Settings for the Postgres provider. */
export type Settings = {
/** Database URL. */
DB_URL: string;
};

let pgClient: undefined | PostgresClient = undefined;

const postgresContext = {
meta(): MetaResult {
return {
name: 'Postgres',
mentions: { label: 'Search by schema name...' },
};
},

async initializePGClient(settingsInput: Settings) {
if (pgClient === undefined) {
pgClient = new PostgresClient(settingsInput.DB_URL);
await pgClient.initializePGData();
}
},

async mentions(
params: MentionsParams,
settingsInput: Settings
): Promise<MentionsResult> {
await this.initializePGClient(settingsInput);
if (!pgClient) {
return [];
}
const userQuery = params.query ?? '';
const schemas = pgClient.getSchemas();
const schemaList = schemas.filter((schema) => schema.includes(userQuery));
if (!schemaList) {
return [];
}
const mentionRes: MentionsResult = [];
for (const schema of schemaList) {
mentionRes.push({
title: schema,
uri: schema,
data: {
schema,
},
});
}
return mentionRes;
},

async items(
params: ItemsParams,
settingsInput: Settings
): Promise<ItemsResult> {
await this.initializePGClient(settingsInput);
if (!pgClient) {
return [];
}
const schema = params.mention?.data?.schema as string;
let message = params.message || '';
console.log({ schema, message });

const schemaContext = await pgClient.getSchema(schema);
return schemaContext;
},
};

export default postgresContext;
29 changes: 29 additions & 0 deletions provider/postgres/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@openctx/provider-postgres",
"version": "0.0.1",
"description": "Context from your Postgres for code AI and editors (OpenCtx provider)",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/sourcegraph/openctx",
"directory": "provider/postgres"
},
"type": "module",
"main": "dist/bundle.js",
"types": "dist/index.d.ts",
"files": [
"dist/bundle.js",
"dist/index.d.ts"
],
"sideEffects": false,
"scripts": {
"bundle": "tsc --build && esbuild --log-level=error --platform=node --bundle --format=esm --outfile=dist/bundle.js index.ts",
"prepublishOnly": "tsc --build --clean && npm run --silent bundle",
"test": "vitest"
},
"dependencies": {
"dedent": "^1.5.3",
"@openctx/provider": "workspace:*",
"postgres": "^3.4.4"
}
}
11 changes: 11 additions & 0 deletions provider/postgres/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../.config/tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "dist",
"lib": ["ESNext"],
},
"include": ["*.ts"],
"exclude": ["dist", "vitest.config.ts"],
"references": [{ "path": "../../lib/provider" }],
}
3 changes: 3 additions & 0 deletions provider/postgres/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({})
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
{ "path": "provider/hello-world" },
{ "path": "provider/links" },
{ "path": "provider/notion" },
{ "path": "provider/postgres" },
{ "path": "provider/prometheus" },
{ "path": "provider/sourcegraph-search" },
{ "path": "provider/storybook" },
Expand Down
2 changes: 1 addition & 1 deletion web/content/docs/creating-a-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ You can configure that provider in an OpenCtx client like so:
When developing locally, configure your provider in the client (e.g., VS Code) by using the path to the bundled `.js` file:
```json
"openctx.providers": {
"file:///<path/to/js/bundle>/index.js": true,
"file:///<path/to/js/bundle>/bundle.js": true,
}
```

Expand Down