Skip to content
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
67 changes: 66 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,25 @@ import { GeminiAICanonicalEvaluationStrategy } from './frameworks/gemini/gemini-
import { OpenAICanonicalEvaluationStrategy } from './frameworks/openai/openai-converter';
import { VercelAICanonicalEvaluationStrategy } from './frameworks/vercelai/vercelai-converter';
import {
type CompilePromptResponse,
EvaluationProxyAPIRequest,
EvaluationProxyAPIRequestSchema,
EvaluationRequestV2Schema,
type EvaluationRequestV2,
EvaluationRequestV2Schema,
type EvaluationResponse,
type Framework,
} from './types';

export type {
CompilePromptResponse,
EvaluationProxyAPIRequest,
EvaluationRequestV2,
EvaluationResponse,
Framework,
LLMMessage,
ModelMode,
PolicyTarget,
ToolResponse,
} from './types';

/**
Expand Down Expand Up @@ -418,4 +421,66 @@ export class Qualifire {
const jsonResponse = await response.json();
return jsonResponse as EvaluationResponse;
};

/**
* Compiles a prompt from Qualifire Studio with the specified parameters.
*
* @param promptId - The ID of the prompt to compile.
* @param revisionId - Optional revision ID to use. If not provided, uses the latest revision.
* @param params - Optional dictionary of parameters to substitute in the prompt template.
* @returns A CompilePromptResponse containing the compiled prompt details.
*
* @example
* ```ts
* const qualifire = new Qualifire({ apiKey: 'your_api_key' });
*
* const response = await qualifire.compilePrompt({
* promptId: 'prompt-123',
* revisionId: 'rev-456',
* params: {
* user_name: 'John',
* language: 'French'
* }
* });
*
* console.log(response.messages);
* console.log(response.tools);
* ```
*/
compilePrompt = async ({
promptId,
revisionId,
params,
}: {
promptId: string;
revisionId?: string;
params?: Record<string, string>;
}): Promise<CompilePromptResponse> => {
let url = `${this.baseUrl}/api/v1/studio/prompts/${promptId}/compile`;
if (revisionId) {
url = `${url}?revision=${revisionId}`;
}
Comment on lines +459 to +462

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

URL-encode promptId and revisionId to prevent injection and malformed URLs.

If promptId or revisionId contain special characters (e.g., /, ?, &, #), the URL construction will break or behave unexpectedly. Use encodeURIComponent for safety.

🔧 Proposed fix
-    let url = `${this.baseUrl}/api/v1/studio/prompts/${promptId}/compile`;
+    let url = `${this.baseUrl}/api/v1/studio/prompts/${encodeURIComponent(promptId)}/compile`;
     if (revisionId) {
-      url = `${url}?revision=${revisionId}`;
+      url = `${url}?revision=${encodeURIComponent(revisionId)}`;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let url = `${this.baseUrl}/api/v1/studio/prompts/${promptId}/compile`;
if (revisionId) {
url = `${url}?revision=${revisionId}`;
}
let url = `${this.baseUrl}/api/v1/studio/prompts/${encodeURIComponent(promptId)}/compile`;
if (revisionId) {
url = `${url}?revision=${encodeURIComponent(revisionId)}`;
}
🤖 Prompt for AI Agents
In `@src/index.ts` around lines 459 - 462, The URL construction using this.baseUrl
and the variables promptId/revisionId is vulnerable to malformed URLs when those
values contain special characters; update the code that builds the compile
endpoint (the lines that set url =
`${this.baseUrl}/api/v1/studio/prompts/${promptId}/compile` and the conditional
that appends `?revision=${revisionId}`) to use encodeURIComponent(promptId) and
encodeURIComponent(revisionId) when interpolating those values so both segments
and query parameters are safely encoded.


const headers = {
'Content-Type': 'application/json',
'X-Qualifire-API-Key': this.sdkKey,
};

const body = JSON.stringify({
variables: params || {},
});

const response = await fetch(url, {
method: 'POST',
headers,
body,
});

if (!response.ok) {
throw new Error(`Qualifire API error: ${response.statusText}`);
}

const jsonResponse = await response.json();
return jsonResponse as CompilePromptResponse;
};
}
17 changes: 17 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,20 @@ export type EvaluationResponse = z.infer<typeof EvaluationResponseSchema>;
export type LLMToolDefinition = z.infer<typeof LLMToolDefinitionSchema>;
export type LLMToolCall = z.infer<typeof LLMToolCallSchema>;
export type EvaluationRequestV2 = z.infer<typeof EvaluationRequestV2Schema>;

const ToolResponseSchema = z.object({
type: z.string(),
function: LLMToolDefinitionSchema,
});

const CompilePromptResponseSchema = z.object({
id: z.string(),
name: z.string(),
revision: z.number(),
messages: z.array(LLMMessageSchema),
tools: z.array(ToolResponseSchema),
parameters: z.record(z.string(), z.any()),
});

export type ToolResponse = z.infer<typeof ToolResponseSchema>;
export type CompilePromptResponse = z.infer<typeof CompilePromptResponseSchema>;