inicio de funcion de generación de asignatura
This commit is contained in:
@@ -3,11 +3,9 @@
|
||||
import OpenAI from "npm:openai@6.16.0";
|
||||
import type * as OpenAITypes from "npm:openai@6.16.0";
|
||||
import { createClient, type SupabaseClient } from "npm:@supabase/supabase-js@2";
|
||||
|
||||
// Use non-streaming params to ensure `responses.create` returns a typed Response
|
||||
export type StructuredResponseOptions =
|
||||
OpenAITypes.OpenAI.Responses.ResponseCreateParamsNonStreaming;
|
||||
|
||||
export type StructuredResponseSuccess<TOutput = unknown> = {
|
||||
ok: true;
|
||||
output?: TOutput; // parsed JSON when available
|
||||
@@ -22,7 +20,6 @@ export type StructuredResponseSuccess<TOutput = unknown> = {
|
||||
};
|
||||
openaiRaw: OpenAITypes.OpenAI.Responses.Response; // keep for advanced consumers
|
||||
};
|
||||
|
||||
export type StructuredResponseFailure = {
|
||||
ok: false;
|
||||
code:
|
||||
@@ -33,23 +30,19 @@ export type StructuredResponseFailure = {
|
||||
message: string;
|
||||
cause?: unknown;
|
||||
};
|
||||
|
||||
export type StructuredResponseResult<TOutput = unknown> =
|
||||
| StructuredResponseSuccess<TOutput>
|
||||
| StructuredResponseFailure;
|
||||
|
||||
export interface OpenAIServiceConfig {
|
||||
supabaseUrl: string;
|
||||
serviceRoleKey: string;
|
||||
openAIApiKey: string;
|
||||
bucket?: string; // default: ai-storage
|
||||
}
|
||||
|
||||
export class OpenAIService {
|
||||
private readonly supabase: SupabaseClient;
|
||||
private readonly openai: OpenAI;
|
||||
private readonly bucket: string;
|
||||
|
||||
private constructor(
|
||||
client: SupabaseClient,
|
||||
openai: OpenAI,
|
||||
@@ -59,13 +52,11 @@ export class OpenAIService {
|
||||
this.openai = openai;
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
static fromEnv(): StructuredResponseFailure | OpenAIService {
|
||||
const supabaseUrl = Deno.env.get("SUPABASE_URL") ?? "";
|
||||
const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";
|
||||
const openAIApiKey = Deno.env.get("OPENAI_API_KEY") ?? "";
|
||||
const bucket = Deno.env.get("SUPABASE_BUCKET") ?? "ai-storage";
|
||||
|
||||
if (!supabaseUrl || !serviceRoleKey || !openAIApiKey) {
|
||||
return {
|
||||
ok: false,
|
||||
@@ -74,12 +65,16 @@ export class OpenAIService {
|
||||
"Required env vars missing: SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, OPENAI_API_KEY",
|
||||
};
|
||||
}
|
||||
|
||||
const client = createClient(supabaseUrl, serviceRoleKey);
|
||||
const openai = new OpenAI({ apiKey: openAIApiKey });
|
||||
return new OpenAIService(client, openai, bucket);
|
||||
}
|
||||
|
||||
async createConversation(metadata?: Record<string, string>) {
|
||||
const conversation = await this.openai.conversations.create({
|
||||
metadata,
|
||||
});
|
||||
return conversation;
|
||||
}
|
||||
async createStructuredResponse<TOutput = unknown>(
|
||||
options: StructuredResponseOptions,
|
||||
files?: File[],
|
||||
@@ -91,7 +86,6 @@ export class OpenAIService {
|
||||
const openaiFileIds = await this.uploadFilesToOpenAI(files ?? []);
|
||||
|
||||
const newOptions = { ...options };
|
||||
|
||||
// Attach file references to the request as an extra user message
|
||||
if (openaiFileIds.length > 0) {
|
||||
const fileParts:
|
||||
@@ -100,7 +94,6 @@ export class OpenAIService {
|
||||
type: "input_file",
|
||||
file_id: id,
|
||||
}));
|
||||
|
||||
const arr = Array.isArray(options.input) ? options.input : [];
|
||||
arr.push({
|
||||
role: "user",
|
||||
@@ -119,7 +112,6 @@ export class OpenAIService {
|
||||
const openaiRaw = (await this.openai.responses.create(
|
||||
newOptions as OpenAITypes.OpenAI.Responses.ResponseCreateParamsNonStreaming,
|
||||
)) as OpenAITypes.OpenAI.Responses.Response;
|
||||
|
||||
const { model, id: responseId } = openaiRaw;
|
||||
const usage = openaiRaw?.usage ?? null;
|
||||
const conversationId = (
|
||||
@@ -127,11 +119,9 @@ export class OpenAIService {
|
||||
conversation_id?: string | null;
|
||||
}
|
||||
).conversation_id ?? null;
|
||||
|
||||
// Try to read structured JSON output
|
||||
let output: TOutput | undefined = undefined;
|
||||
let outputText: string | undefined = undefined;
|
||||
|
||||
// Prefer `output_text` if present (SDK convenience)
|
||||
const maybeOutputText = openaiRaw.output_text;
|
||||
if (
|
||||
@@ -141,7 +131,9 @@ export class OpenAIService {
|
||||
outputText = maybeOutputText;
|
||||
try {
|
||||
output = JSON.parse(maybeOutputText) as TOutput;
|
||||
} catch { /* non-JSON text, keep as text only */ }
|
||||
} catch {
|
||||
/* non-JSON text, keep as text only */
|
||||
}
|
||||
} else {
|
||||
// Fallback: attempt to serialize `openaiRaw.output` into text
|
||||
const maybeOutput = openaiRaw.output as unknown;
|
||||
@@ -149,10 +141,11 @@ export class OpenAIService {
|
||||
try {
|
||||
outputText = JSON.stringify(maybeOutput);
|
||||
output = maybeOutput as TOutput;
|
||||
} catch { /* ignore */ }
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
output,
|
||||
@@ -172,11 +165,9 @@ export class OpenAIService {
|
||||
: message.includes("OpenAI file upload failed")
|
||||
? "OpenAIFileUploadFailed"
|
||||
: "OpenAIRequestFailed";
|
||||
|
||||
return { ok: false, code, message, cause: err };
|
||||
}
|
||||
}
|
||||
|
||||
private async uploadFilesToStorage(files: File[]): Promise<string[]> {
|
||||
const paths: string[] = [];
|
||||
for (const file of files) {
|
||||
@@ -188,7 +179,6 @@ export class OpenAIService {
|
||||
contentType: file.type || "application/octet-stream",
|
||||
upsert: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(`Supabase upload failed: ${error.message}`);
|
||||
}
|
||||
@@ -196,7 +186,6 @@ export class OpenAIService {
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
private async uploadFilesToOpenAI(files: File[]): Promise<string[]> {
|
||||
const ids: string[] = [];
|
||||
for (const file of files) {
|
||||
@@ -216,7 +205,6 @@ export class OpenAIService {
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
private sanitizeFilename(name: string): string {
|
||||
return name
|
||||
.normalize("NFD")
|
||||
|
||||
Reference in New Issue
Block a user