Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connection interface #44

Merged
merged 7 commits into from
Aug 26, 2024
Merged
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
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
Loading