Skip to content

Commit

Permalink
Merge pull request #44 from outerbase/bwilmoth/connection-interface
Browse files Browse the repository at this point in the history
Connection interface
  • Loading branch information
Brayden authored Aug 26, 2024
2 parents 0362f53 + 02c2038 commit 1b63f92
Show file tree
Hide file tree
Showing 24 changed files with 1,180 additions and 387 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@outerbase/sdk",
"version": "1.0.13",
"version": "1.0.15",
"description": "",
"main": "dist/index.js",
"module": "dist/index.js",
Expand Down
77 changes: 60 additions & 17 deletions playground/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,71 @@
import { CloudflareD1Connection, Outerbase, NeonHttpConnection, OuterbaseConnection, equalsNumber } from '../dist/index.js';
import { CloudflareD1Connection, Outerbase, NeonHttpConnection, OuterbaseConnection, equalsNumber, equals } from '../dist/index.js';
import express from 'express';
import { ColumnDataType } from '../dist/query-builder/index.js';

const app = express();
const port = 4000;

app.get('/', async (req, res) => {
app.get('/test/cloudflare', async (req, res) => {
// Establish connection to your provider database
const d1 = new CloudflareD1Connection('API_KEY', 'ACCOUNT_ID', 'DATABASE_ID');
const neon = new NeonHttpConnection({
databaseUrl: 'postgresql://USER:[email protected]/neondb?sslmode=require'
const d1 = new CloudflareD1Connection({
apiKey: '',
accountId: '',
databaseId: ''
});

// Create an Outerbase instance from the data connection
await neon.connect();
const db = Outerbase(neon);

// SELECT:
// let { data, query } = await db.selectFrom([
// { table: 'playing_with_neon', columns: ['id', 'name', 'value'] }
// ])
// .where(equalsNumber('id', 1))
// .query()

let { data } = await db.queryRaw('SELECT * FROM playing_with_neon WHERE id = $1', ['1']);
const db = Outerbase(d1);
// const dbSchema = await d1.fetchDatabaseSchema()

// const { data, query } = await db.selectFrom([
// { table: 'test2', columns: ['*'] }
// ]).query()

// let { data, query } = await db
// .insert({ fname: 'John' })
// .into('test2')
// .returning(['id'])
// .query();

// let { data, query } = await db
// .update({ fname: 'Johnny' })
// .into('test2')
// .where(equals('id', '3', d1.dialect))
// .query();

// let { data, query } = await db
// .deleteFrom('test2')
// .where(equals('id', '3'))
// .query();

let data = {}
let query = await db
.createTable('test3')
.schema('public')
.columns([
{ name: 'id', type: ColumnDataType.NUMBER, primaryKey: true },
{ name: 'fname', type: ColumnDataType.STRING }
])
.toString();

// let data = {}
// let query = await db
// .renameTable('test3', 'test4')
// .toString();

// let data = {}
// let query = await db
// .dropTable('test4')
// .toString();

console.log('Running Query: ', query)

// db.
// - ACTION
// - CONDITIONS
// - RETURNING
// - query() / toString()

// let { data } = await db.queryRaw('SELECT * FROM playing_with_neon WHERE id = $1', ['1']);
res.json(data);
});

Expand Down
1 change: 1 addition & 0 deletions playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "index.js",
"type": "module",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
Expand Down
29 changes: 29 additions & 0 deletions src/ai/prompts/ob1/1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const PROMPT = `
A user is going to tell us what they want from a user interface. Another system will produce the interface, your job is to identify what kind of API endpoints are required to make that user interface function fully. This includes a name, description, and what verb type the API endpoint should contain.
For example, if a user prompts for the following:
"Create a login page for my users"
Then the expected output would be:
\`\`\`
[
{
"name": "Login User",
"description": "An endpoint that authenticates the users login credentials and returns a success or failure",
"verb": "POST",
"path": "/login"
}
]
\`\`\`
- Filter out anything that is just a redirect to another page and would *not* be a functional endpoint.
- If it's code and the function is derived from an \`href\` then filter it out.
- If the code does something with the interface and would not need to make a data request for it, do not include it.
- When defining a \`path\` that contains dynamic values use handlebars (two curly braces) instead of one like: \`{{id}}\`
When you create the path, do not add any file extensions to the end of it.
Important: Only respond in a JSON format, do not add any additional text.
`
40 changes: 40 additions & 0 deletions src/ai/prompts/ob1/2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
export const PROMPT = `
You will receive a database type (such as SQLite, Postgres or MySQL) to use when creating the SQL statement, and an array of endpoints that are required to make a user interface function. Your job is to understand those endpoints by their name and description, and convert those requirements into a SQL statement that is supported on the mentioned database type by the user to create the required tables and columns to make it function.
For example, the below object will be your input.
\`\`\`
Database Type: sqlite
[
{
"name": "Login user",
"description": "An endpoint that takes in a username and password and authenticates to see if the user account exists"
},
]
\`\`\`
An expected output would be something like the following:
\`\`\`
CREATE TABLE "user" (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
email TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
\`\`\`
Remember we need to create tables and columns that fulfill all of the endpoints we are creating. Do *not* include anything in your response except valid SQLite SQL statements.
*Rules:*
- All table names should be singular instead of plurarl. For example, prefer \`user\` instead of \`users\`.
- If the name of a table is a reserved keyword of SQLite (such as \`user\`) then wrap the table name in double quotes.
- *Critically Important:* the response must ONLY contain SQL and not contain any additional descriptive text about it.
Good Example of wrapping reserved keyword \`user\` in double quotes.
\`\`\`
CREATE TABLE "user"
\`\`\`
`
3 changes: 3 additions & 0 deletions src/ai/prompts/ob1/3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PROMPT = `
`
3 changes: 3 additions & 0 deletions src/ai/prompts/ob1/4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PROMPT = `
`
16 changes: 16 additions & 0 deletions src/ai/prompts/text-to-sql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const SQL_PROMPT = `
Return a SQL statement based on the user prompt. Only ever return a single SQL statement. Do not respond with any additional text or information.
Database Schema: {{DATABASE_SCHEMA}}
`

// Export a function that takes a user prompt and returns a SQL statement.
export function prompt(schema: any): string {
// If the schema is a JSON object, stringify it
if (typeof schema === "object") {
schema = JSON.stringify(schema);
}

const current = SQL_PROMPT;
return current.replace("{{DATABASE_SCHEMA}}", schema);
}
3 changes: 3 additions & 0 deletions src/ai/prompts/text-to-worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const PROMPT = `
Return a Cloudflare worker function code snippet based on the user prompt. Only ever return a single code snippet. Do not respond with any additional text or information.
`
40 changes: 40 additions & 0 deletions src/ai/providers/anthropic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { OperationResponse } from "src/connections";
import { AiConversation, AiMessage, AiProvider } from "./index";

export class ClaudeAi implements AiProvider {
model: string = "claude-v1";
apiKey: string = "YOUR_ANTHROPIC_API_KEY";

constructor(_?: { model: string, apiKey: string }) {
if (!_) return;
this.model = _.model;
this.apiKey = _.apiKey;
}

async startConversation(systemPrompt: string, message: string): Promise<OperationResponse> {
const response = await fetch(
`https://api.anthropic.com/v1/complete`,
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
model: this.model, // Specify the Claude model version
prompt: `${systemPrompt}\n\nHuman: ${message}\n\nAssistant:`,
max_tokens_to_sample: 300,
stop_sequences: ["\n\nHuman:"]
}),
}
);

const result = await response.json();
console.log('Result: ', result);
return {
success: response.ok,
error: result.error ? result.error : undefined,
data: result.completion || ""
};
}
}
47 changes: 47 additions & 0 deletions src/ai/providers/cloudflare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { OperationResponse } from "src/connections";
import { AiConversation, AiMessage, AiProvider } from "./index";

export class CloudflareAi implements AiProvider {
model: string = "llama-3-8b-instruct";
apiKey: string = "YOUR_CLOUDFLARE_API_KEY";

constructor(_?: { model: string, apiKey: string }) {
if (!_) return;
this.model = _.model;
this.apiKey = _.apiKey;
}

async startConversation(systemPrompt: string, message: string): Promise<OperationResponse> {
const response = await fetch(
`https://api.cloudflare.com/client/v4/accounts/68599e3cad89422e9c74bd9b829754bd/ai/run/@cf/meta/${this.model}`,
{
headers: { Authorization: `Bearer ${this.apiKey}` },
method: "POST",
body: JSON.stringify({
messages: [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: message
}
]
}),
}
);

const result = await response.json();
console.log('Result: ', result);
return {
success: result.success,
error: result.errors,
data: result.result.response
};
}

continueConversation(message: string): Promise<any> {
return Promise.resolve();
}
}
21 changes: 21 additions & 0 deletions src/ai/providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { OperationResponse } from "../../connections"

export type AiConversation = {
id: string
messages: AiMessage[]
}

export type AiMessage = {
id: string
text: string
from: 'user' | 'system' | 'role'
}

export interface AiProvider {
startConversation: (systemPrompt: string, message: string) => Promise<OperationResponse>
continueConversation?: (message: string) => Promise<OperationResponse>

// Optional methods
updateSystemPrompt?: (message: string) => Promise<OperationResponse>
removeMessage?: (conversationId: string, messageId: string) => Promise<OperationResponse>
}
47 changes: 47 additions & 0 deletions src/ai/providers/openai.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { OperationResponse } from "src/connections";
import { AiConversation, AiMessage, AiProvider } from "./index";

export class OpenAi implements AiProvider {
model: string = "gpt-3.5-turbo";
apiKey: string = "YOUR_OPENAI_API_KEY";

constructor(_?: { model: string, apiKey: string }) {
if (!_) return;
this.model = _.model;
this.apiKey = _.apiKey;
}

async startConversation(systemPrompt: string, message: string): Promise<OperationResponse> {
const response = await fetch(
`https://api.openai.com/v1/chat/completions`,
{
headers: {
Authorization: `Bearer ${this.apiKey}`,
"Content-Type": "application/json"
},
method: "POST",
body: JSON.stringify({
model: this.model,
messages: [
{
role: 'system',
content: systemPrompt
},
{
role: 'user',
content: message
}
]
}),
}
);

const result = await response.json();

return {
success: response.ok,
error: result.error ? result.error : null,
data: result.choices?.[0]?.message?.content || ""
};
}
}
Loading

0 comments on commit 1b63f92

Please sign in to comment.