Skip to content

Commit

Permalink
chore: set up some logging and rollbar error handling for ai endpoints (
Browse files Browse the repository at this point in the history
#3582)

* chore: set up some logging and rollbar error handling for ai endpoints

* fix: logging

* chore: update rollbar

* chore: next 14

* chore: remove rollbar from ai streaming

* chore: check rewust body and make sure biome formats files better

* chore: create search tracking

* chore: run migrations on previews
  • Loading branch information
SiTaggart authored Oct 30, 2023
1 parent 2059b17 commit 701995f
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 255 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/on_pull_request_supabase.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ jobs:
test:
runs-on: ubuntu-latest

env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
SUPABASE_DB_PASSWORD: ${{ secrets.SUPABASE_STAGING_DB_PASSWORD }}
SUPABASE_PROJECT_ID: ${{ secrets.SUPABASE_STAGING_PROJECT_ID }}

steps:
- name: Checkout Repo
uses: actions/checkout@v4
Expand Down Expand Up @@ -49,6 +54,12 @@ jobs:
with:
version: latest

- name: Link Supabase project
run: yarn workspace @twilio-paste/backend supabase link --project-ref $SUPABASE_PROJECT_ID

- name: Run migrations on staging
run: yarn workspace @twilio-paste/backend supabase db push

- name: Start Supabase local development setup
run: yarn start:backend:dev

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
create table "public"."queries" (
"id" bigint generated by default as identity not null,
"created_at" timestamp with time zone not null default now(),
"query_string" character varying,
"type" text
);


alter table "public"."queries" enable row level security;

alter table "public"."page" enable row level security;

alter table "public"."page_section" enable row level security;

CREATE UNIQUE INDEX queries_pkey ON public.queries USING btree (id);

alter table "public"."queries" add constraint "queries_pkey" PRIMARY KEY using index "queries_pkey";

create policy "Public access"
on "public"."page"
as permissive
for all
to public
using (true)
with check (true);


create policy "Public access"
on "public"."page_section"
as permissive
for all
to public
using (true)
with check (true);


create policy "Enable read access for all"
on "public"."queries"
as permissive
for select
to public
using (true);


create policy "Enable write access for all"
on "public"."queries"
as permissive
for insert
to public
with check (true);



21 changes: 21 additions & 0 deletions apps/backend/supabase/schema.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,27 @@ export interface Database {
}
]
}
queries: {
Row: {
created_at: string
id: number
query_string: string | null
type: string | null
}
Insert: {
created_at?: string
id?: number
query_string?: string | null
type?: string | null
}
Update: {
created_at?: string
id?: number
query_string?: string | null
type?: string | null
}
Relationships: []
}
}
Views: {
[_ in never]: never
Expand Down
24 changes: 12 additions & 12 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@
"files": {
"maxSize": 5242880,
"ignore": [
"node_modules",
"bower_components",
"dist",
"bin",
"docs",
".temp",
"__testfixtures__",
"__fixtures__",
"node_modules/",
"bower_components/",
"dist/",
"bin/",
"docs/",
".temp/",
"__testfixtures__/",
"__fixtures__/",
"packages/paste-icons/cjs",
"packages/paste-icons/esm",
"packages/paste-icons/json",
"packages/paste-theme-designer/public",
"packages/paste-theme-designer/out",
"packages/paste-token-contrast-checker/public",
"packages/paste-website/data",
".cache",
".next",
".netlify",
".yarn",
".cache/",
".next/",
".netlify/",
".yarn/",
"packages/**/dist/*",
"tsconfig.build.tsbuildinfo",
"**/*.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/paste-theme-designer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"@datadog/browser-rum": "^4.46.0",
"@twilio-paste/core": "^20.0.0",
"@twilio-paste/icons": "^12.0.0",
"next": "^13.1.6",
"next": "^14.0.0",
"react": "^18.0.0",
"react-color": "^2.19.3"
}
Expand Down
3 changes: 0 additions & 3 deletions packages/paste-website/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ const nextConfig = {
compiler: {
emotion: true,
},
experimental: {
legacyBrowsers: false,
},
// https://nextjs.org/docs/pages/api-reference/next-config-js/headers
async headers() {
return [
Expand Down
12 changes: 6 additions & 6 deletions packages/paste-website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"@mdx-js/loader": "^1.6.22",
"@mdx-js/mdx": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@next/bundle-analyzer": "^13.1.6",
"@next/mdx": "^13.1.6",
"@next/bundle-analyzer": "^14.0.0",
"@next/mdx": "^14.0.0",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-graphql": "^4.0.0",
"@sparticuz/chromium": "^110.0.0",
Expand Down Expand Up @@ -157,7 +157,7 @@
"mdast-util-to-string": "^3.1.1",
"micromark-extension-mdxjs": "^2.0.0",
"minimist": "^1.2.8",
"next": "^13.1.6",
"next": "^14.0.0",
"openai": "^4.10.0",
"openai-edge": "^1.2.2",
"pretty-format": "^28.1.0",
Expand All @@ -173,18 +173,18 @@
"react-scrollspy": "^3.4.0",
"react-visibility-sensor": "5.1.1",
"remark-gfm": "^3.0.1",
"rollbar": "^2.25.0",
"rollbar": "^2.26.2",
"sharp": "^0.32.5",
"typescript": "^4.9.4",
"unist-builder": "^4.0.0",
"unist-util-filter": "^5.0.1",
"unist-util-visit-esm": "npm:unist-util-visit@^4.1.2",
"use-resize-observer": "^9.1.0",
"uuid": "^9.0.1",
"winston": "^3.8.1"
"winston": "^3.11.0"
},
"devDependencies": {
"@next/eslint-plugin-next": "^13.1.6",
"@next/eslint-plugin-next": "^14.0.0",
"@storybook/react": "7.0.6",
"@testing-library/react": "^13.4.0",
"tsx": "^3.12.10"
Expand Down
47 changes: 44 additions & 3 deletions packages/paste-website/src/pages/api/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,18 @@ const config = new Configuration({
});
const openai = new OpenAIApi(config);

/**
* Because we're using an edge function for streaming we can't use winston for logging
* or rollbar for error reporting. Instead we'll use console.log and console.error and
* Datadog synthetics to monitor up time.
*/
export const runtime = "edge";

const LOG_PREFIX = "[/api/ai]:";

export default async function handler(req: NextRequest): Promise<void | Response> {
// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Incoming request`);
try {
if (!openAiKey) {
throw new ApplicationError("Missing environment variable OPENAI_API_KEY");
Expand All @@ -64,12 +73,16 @@ export default async function handler(req: NextRequest): Promise<void | Response
}

const requestData = await req.json();
// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Request data`, { requestData });

if (!requestData) {
throw new UserError("Missing request data");
}

const { prompt: query, secret } = requestData;
// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} User query`, { query });

if (!secret || secret !== openAiSecret) {
throw new UserError("Incorrect 'secret' in request data");
Expand All @@ -81,8 +94,14 @@ export default async function handler(req: NextRequest): Promise<void | Response

const supabaseClient = createClient(supabaseUrl, supabaseServiceKey);

// Moderate the content to comply with OpenAI T&C
const sanitizedQuery = query.trim();
// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Sanitized query`, { sanitizedQuery });

// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Moderate user prompt`);

// Moderate the content to comply with OpenAI T&C
const moderationResponse: CreateModerationResponse = await openai
.createModeration({ input: sanitizedQuery })
.then((res: any) => res.json());
Expand All @@ -93,6 +112,8 @@ export default async function handler(req: NextRequest): Promise<void | Response
throw new ApplicationError("Failed to moderate content", moderationResponse.error.message);
}
const [results] = moderationResponse.results;
// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Moderated prompt`, { results });

if (results.flagged) {
throw new UserError("Flagged content", {
Expand All @@ -101,6 +122,9 @@ export default async function handler(req: NextRequest): Promise<void | Response
});
}

// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Reqesting openai embedding`);

// Create embedding from query
const embeddingResponse = await openai.createEmbedding({
model: "text-embedding-ada-002",
Expand All @@ -115,6 +139,9 @@ export default async function handler(req: NextRequest): Promise<void | Response
data: [{ embedding }],
}: CreateEmbeddingResponse = await embeddingResponse.json();

// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Request Page sections based on embeddings`);

const { error: matchError, data: pageSections } = await supabaseClient.rpc("match_page_sections_for_ai", {
embedding,
/* eslint-disable camelcase */
Expand All @@ -128,6 +155,9 @@ export default async function handler(req: NextRequest): Promise<void | Response
throw new ApplicationError("Failed to match page sections", matchError);
}

// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Returned ${pageSections.length} page sections`);

const tokenizer = new GPT3Tokenizer({ type: "gpt3" });
let tokenCount = 0;
let contextText = "";
Expand All @@ -144,6 +174,9 @@ export default async function handler(req: NextRequest): Promise<void | Response
contextText += `${content.trim()}\n---\n`;
}

// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Context text: ${contextText}`);

const prompt = codeBlock`
${oneLine`
Your name is PasteBot. You are a very enthusiastic Paste design system
Expand All @@ -168,6 +201,8 @@ export default async function handler(req: NextRequest): Promise<void | Response
role: "user",
content: prompt,
};
// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Request chat completion`);

const response = await openai.createChatCompletion({
model: "gpt-4",
Expand All @@ -183,13 +218,19 @@ export default async function handler(req: NextRequest): Promise<void | Response
throw new ApplicationError("Failed to generate completion", error);
}

// eslint-disable-next-line no-console
console.log(`${LOG_PREFIX} Open ai Returned response`);

// Transform the response into a readable stream
const stream = OpenAIStream(response);

// Return a StreamingTextResponse, which can be consumed by the client
return new StreamingTextResponse(stream);
} catch (error: unknown) {
if (error instanceof UserError) {
// eslint-disable-next-line no-console
console.error(`${LOG_PREFIX} User error`, { error });

return new Response(
JSON.stringify({
error: error.message,
Expand All @@ -203,11 +244,11 @@ export default async function handler(req: NextRequest): Promise<void | Response
} else if (error instanceof ApplicationError) {
// Print out application errors with their additional data
// eslint-disable-next-line no-console
console.error(`${error.message}: ${JSON.stringify(error.data)}`);
console.error(`${LOG_PREFIX} ${error.message}: ${JSON.stringify(error.data)}`);
} else {
// Print out unexpected errors as is to help with debugging
// eslint-disable-next-line no-console
console.error(error);
console.error(`${LOG_PREFIX} ${error}`);
}

return new Response(
Expand Down
Loading

0 comments on commit 701995f

Please sign in to comment.