Merge pull request 'chore: Remove json markdown stuff' (#2) from jank/simple-ai-provider:main into main
Reviewed-on: #2
This commit is contained in:
@@ -22,7 +22,7 @@ export interface AIProviderConfig {
|
|||||||
/**
|
/**
|
||||||
* Message role types supported by AI providers
|
* Message role types supported by AI providers
|
||||||
*/
|
*/
|
||||||
export type MessageRole = 'system' | 'user' | 'assistant';
|
export type MessageRole = "system" | "user" | "assistant";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Individual message in a conversation
|
* Individual message in a conversation
|
||||||
@@ -122,13 +122,13 @@ export interface CompletionChunk {
|
|||||||
* Error types that can occur during AI operations
|
* Error types that can occur during AI operations
|
||||||
*/
|
*/
|
||||||
export enum AIErrorType {
|
export enum AIErrorType {
|
||||||
AUTHENTICATION = 'authentication',
|
AUTHENTICATION = "authentication",
|
||||||
RATE_LIMIT = 'rate_limit',
|
RATE_LIMIT = "rate_limit",
|
||||||
INVALID_REQUEST = 'invalid_request',
|
INVALID_REQUEST = "invalid_request",
|
||||||
MODEL_NOT_FOUND = 'model_not_found',
|
MODEL_NOT_FOUND = "model_not_found",
|
||||||
NETWORK = 'network',
|
NETWORK = "network",
|
||||||
TIMEOUT = 'timeout',
|
TIMEOUT = "timeout",
|
||||||
UNKNOWN = 'unknown'
|
UNKNOWN = "unknown",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -139,10 +139,10 @@ export class AIProviderError extends Error {
|
|||||||
message: string,
|
message: string,
|
||||||
public type: AIErrorType,
|
public type: AIErrorType,
|
||||||
public statusCode?: number,
|
public statusCode?: number,
|
||||||
public originalError?: Error
|
public originalError?: Error,
|
||||||
) {
|
) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = 'AIProviderError';
|
this.name = "AIProviderError";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,18 +164,18 @@ export interface ProviderInfo {
|
|||||||
capabilities?: Record<string, any>;
|
capabilities?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// RESPONSE TYPE UTILITIES
|
// RESPONSE TYPE UTILITIES
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a response type definition for structured AI outputs
|
* Creates a response type definition for structured AI outputs
|
||||||
*
|
*
|
||||||
* @param description - Human-readable description of the expected format
|
* @param description - Human-readable description of the expected format
|
||||||
* @param example - Optional example of the expected response structure
|
* @param example - Optional example of the expected response structure
|
||||||
* @param strictJson - Whether to enforce strict JSON formatting (default: true)
|
* @param strictJson - Whether to enforce strict JSON formatting (default: true)
|
||||||
* @returns ResponseType object for use in completion requests
|
* @returns ResponseType object for use in completion requests
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* const userType = createResponseType(
|
* const userType = createResponseType(
|
||||||
@@ -192,51 +192,58 @@ export interface ProviderInfo {
|
|||||||
export function createResponseType<T = any>(
|
export function createResponseType<T = any>(
|
||||||
description: string,
|
description: string,
|
||||||
example?: T,
|
example?: T,
|
||||||
strictJson: boolean = true
|
strictJson: boolean = true,
|
||||||
): ResponseType<T> {
|
): ResponseType<T> {
|
||||||
return {
|
return {
|
||||||
description,
|
description,
|
||||||
example,
|
example,
|
||||||
strictJson
|
strictJson,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a system prompt instruction for structured output based on response type
|
* Generates a system prompt instruction for structured output based on response type
|
||||||
*
|
*
|
||||||
* @param responseType - The response type definition
|
* @param responseType - The response type definition
|
||||||
* @returns Formatted system prompt instruction
|
* @returns Formatted system prompt instruction
|
||||||
*/
|
*/
|
||||||
export function generateResponseTypePrompt(responseType: ResponseType): string {
|
export function generateResponseTypePrompt(responseType: ResponseType): string {
|
||||||
const { typeDefinition, description, example, strictJson } = responseType;
|
const { typeDefinition, description, example, strictJson } = responseType;
|
||||||
|
|
||||||
let prompt = 'You are an AI assistant that must respond with a JSON object.';
|
let prompt = "You are an AI assistant that must respond with a JSON object.";
|
||||||
|
|
||||||
if (typeDefinition) {
|
if (typeDefinition) {
|
||||||
prompt += ' The JSON object must strictly adhere to the following TypeScript type definition:\n\n';
|
prompt +=
|
||||||
prompt += 'Type Definition:\n```typescript\n' + typeDefinition + '\n```\n\n';
|
" The JSON object must strictly adhere to the following TypeScript type definition:\n\n";
|
||||||
|
prompt +=
|
||||||
|
"Type Definition:\n```typescript\n" + typeDefinition + "\n```\n\n";
|
||||||
} else {
|
} else {
|
||||||
prompt += '\n\n';
|
prompt += "\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
prompt += 'Description: ' + description + '\n\n';
|
prompt += "Description: " + description + "\n\n";
|
||||||
|
|
||||||
if (example) {
|
if (example) {
|
||||||
prompt += 'Example of the expected JSON output:\n```json\n' + JSON.stringify(example, null, 2) + '\n```\n\n';
|
prompt +=
|
||||||
|
"Example of the expected JSON output:\n```json\n" +
|
||||||
|
JSON.stringify(example, null, 2) +
|
||||||
|
"\n```\n\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strictJson) {
|
if (strictJson) {
|
||||||
prompt += 'IMPORTANT: Your entire response must be a single, valid JSON object. Do not include any additional text, explanations, or markdown formatting before or after the JSON object.';
|
prompt +=
|
||||||
|
"IMPORTANT: Your entire response must be a single, valid JSON object. Do not include any additional text, explanations, or markdown formatting before or after the JSON object.";
|
||||||
} else {
|
} else {
|
||||||
prompt += 'Your response should contain a JSON object that follows the structure defined above.';
|
prompt +=
|
||||||
|
"Your response should contain a JSON object that follows the structure defined above.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses and validates that a response matches the expected type structure
|
* Parses and validates that a response matches the expected type structure
|
||||||
*
|
*
|
||||||
* @param response - The response content to validate
|
* @param response - The response content to validate
|
||||||
* @param responseType - The expected response type
|
* @param responseType - The expected response type
|
||||||
* @returns The parsed and validated data object
|
* @returns The parsed and validated data object
|
||||||
@@ -244,22 +251,24 @@ export function generateResponseTypePrompt(responseType: ResponseType): string {
|
|||||||
*/
|
*/
|
||||||
export function parseAndValidateResponseType<T = any>(
|
export function parseAndValidateResponseType<T = any>(
|
||||||
response: string,
|
response: string,
|
||||||
responseType: ResponseType<T>
|
responseType: ResponseType<T>,
|
||||||
): T {
|
): T {
|
||||||
try {
|
try {
|
||||||
// Attempt to parse the JSON response
|
// Attempt to parse the JSON response
|
||||||
const parsed = JSON.parse(response);
|
const parsed = JSON.parse(
|
||||||
|
response.replaceAll("```json", "").replaceAll("```", ""),
|
||||||
|
);
|
||||||
|
|
||||||
// Basic validation: ensure it's a non-null object.
|
// Basic validation: ensure it's a non-null object.
|
||||||
// For more robust validation, a library like Zod or Ajv could be used
|
// For more robust validation, a library like Zod or Ajv could be used
|
||||||
// based on the typeDefinition, but that's beyond the current scope.
|
// based on the typeDefinition, but that's beyond the current scope.
|
||||||
if (typeof parsed !== 'object' || parsed === null) {
|
if (typeof parsed !== "object" || parsed === null) {
|
||||||
throw new Error('Response must be a JSON object');
|
throw new Error("Response must be a JSON object");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here you could add more sophisticated validation if needed, e.g.,
|
// Here you could add more sophisticated validation if needed, e.g.,
|
||||||
// checking keys against the responseType.typeDefinition
|
// checking keys against the responseType.typeDefinition
|
||||||
|
|
||||||
return parsed as T;
|
return parsed as T;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Wrap parsing/validation errors in a standardized AIProviderError
|
// Wrap parsing/validation errors in a standardized AIProviderError
|
||||||
@@ -267,7 +276,7 @@ export function parseAndValidateResponseType<T = any>(
|
|||||||
`Failed to parse or validate structured response: ${(error as Error).message}`,
|
`Failed to parse or validate structured response: ${(error as Error).message}`,
|
||||||
AIErrorType.INVALID_REQUEST,
|
AIErrorType.INVALID_REQUEST,
|
||||||
undefined,
|
undefined,
|
||||||
error as Error
|
error as Error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user