-
Notifications
You must be signed in to change notification settings - Fork 58
Add endpoints for interacting with env tags in ts. #1229
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -89,6 +89,15 @@ import iso, { IsoAsyncLocalStorage } from "./isomorph"; | |
| import { canUseDiskCache, DiskCache } from "./prompt-cache/disk-cache"; | ||
| import { LRUCache } from "./prompt-cache/lru-cache"; | ||
| import { PromptCache } from "./prompt-cache/prompt-cache"; | ||
| import { | ||
| clearPromptEnvironment, | ||
| listPromptEnvironments, | ||
| setPromptEnvironment, | ||
| type ClearPromptEnvironmentOptions, | ||
| type ListPromptEnvironmentsOptions, | ||
| type PromptEnvironmentAssociation, | ||
| type SetPromptEnvironmentOptions, | ||
| } from "./prompt-environments"; | ||
| import { | ||
| addAzureBlobHeaders, | ||
| getCurrentUnixTimestamp, | ||
|
|
@@ -6543,6 +6552,46 @@ export class Prompt< | |
| return this.getParsedPromptData()!; | ||
| } | ||
|
|
||
| private requireId(): string { | ||
| const id = this.id as string | undefined; | ||
| if (!id) { | ||
| throw new Error( | ||
| "Prompt id is required to manage environments. Load the prompt from the API first.", | ||
| ); | ||
| } | ||
| return id; | ||
| } | ||
|
|
||
| public listPromptEnvironments( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not sure i'd expect these methods here if the framework2 already has this. 🤔 if we want to keep them would imagine something like: (Prompt seemed duplicative) |
||
| this: Prompt<true, HasVersion>, | ||
| options: Omit<ListPromptEnvironmentsOptions, "promptId"> = {}, | ||
| ): Promise<PromptEnvironmentAssociation[]> { | ||
| return listPromptEnvironments({ | ||
| ...options, | ||
| promptId: this.requireId(), | ||
| }); | ||
| } | ||
|
|
||
| public setPromptEnvironment( | ||
| this: Prompt<true, HasVersion>, | ||
| options: Omit<SetPromptEnvironmentOptions, "promptId">, | ||
| ): Promise<PromptEnvironmentAssociation> { | ||
| return setPromptEnvironment({ | ||
| ...options, | ||
| promptId: this.requireId(), | ||
| }); | ||
| } | ||
|
|
||
| public clearPromptEnvironment( | ||
| this: Prompt<true, HasVersion>, | ||
| options: Omit<ClearPromptEnvironmentOptions, "promptId">, | ||
| ): Promise<PromptEnvironmentAssociation> { | ||
| return clearPromptEnvironment({ | ||
| ...options, | ||
| promptId: this.requireId(), | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Build the prompt with the given formatting options. The args you pass in will | ||
| * be forwarded to the mustache template that defines the prompt and rendered with | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
| import { | ||
| BraintrustState, | ||
| FullLoginOptions, | ||
| _internalGetGlobalState, | ||
| } from "./logger"; | ||
|
|
||
| export interface PromptEnvironmentAssociation { | ||
| id: string; | ||
| object_type: string; | ||
| object_id: string; | ||
| object_version: string; | ||
| environment_slug: string; | ||
| created: string; | ||
| } | ||
|
|
||
| export type ListPromptEnvironmentsOptions = FullLoginOptions & { | ||
| promptId: string; | ||
| state?: BraintrustState; | ||
| }; | ||
|
|
||
| export type SetPromptEnvironmentOptions = FullLoginOptions & { | ||
| promptId: string; | ||
| environmentSlug: string; | ||
| version: string; | ||
| state?: BraintrustState; | ||
| }; | ||
|
|
||
| export type ClearPromptEnvironmentOptions = FullLoginOptions & { | ||
| promptId: string; | ||
| environmentSlug: string; | ||
| state?: BraintrustState; | ||
| }; | ||
|
|
||
| /** | ||
| * List all environment associations for a prompt. | ||
| * | ||
| * @param options Options for the request | ||
| * @param options.promptId The ID of the prompt to list environment associations for | ||
| * @param options.apiKey The API key to use. If not specified, will use the `BRAINTRUST_API_KEY` environment variable. | ||
| * @param options.appUrl The URL of the Braintrust App. Defaults to https://www.braintrust.dev. | ||
| * @param options.orgName (Optional) The name of a specific organization to connect to. | ||
| * @returns A list of environment associations for the prompt | ||
| * | ||
| * @example | ||
| * ```javascript | ||
| * const associations = await listPromptEnvironments({ | ||
| * promptId: "prompt-uuid", | ||
| * }); | ||
| * console.log(associations); | ||
| * // [{ environment_slug: "production", object_version: "123", ... }] | ||
| * ``` | ||
| */ | ||
| export async function listPromptEnvironments({ | ||
| promptId, | ||
| appUrl, | ||
| apiKey, | ||
| orgName, | ||
| fetch, | ||
| forceLogin, | ||
| state: stateArg, | ||
| }: ListPromptEnvironmentsOptions): Promise<PromptEnvironmentAssociation[]> { | ||
| const state = stateArg ?? _internalGetGlobalState(); | ||
|
|
||
| await state.login({ | ||
| orgName, | ||
| apiKey, | ||
| appUrl, | ||
| fetch, | ||
| forceLogin, | ||
| }); | ||
|
|
||
| const response = await state | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing try catch? perhaps better error message |
||
| .apiConn() | ||
| .get_json( | ||
| `environment-object/prompt/${promptId}`, | ||
| orgName ? { org_name: orgName } : {}, | ||
| ); | ||
|
|
||
| return (response.objects ?? []) as PromptEnvironmentAssociation[]; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would this hide an upstream error? |
||
| } | ||
|
|
||
| /** | ||
| * Set (or update) the prompt version associated with an environment. | ||
| * If an association already exists, it will be updated. Otherwise, a new one will be created. | ||
| * | ||
| * @param options Options for the request | ||
| * @param options.promptId The ID of the prompt | ||
| * @param options.environmentSlug The environment slug to set (e.g., "production", "staging") | ||
| * @param options.version The version (xact_id) of the prompt to associate with this environment | ||
| * @param options.apiKey The API key to use. If not specified, will use the `BRAINTRUST_API_KEY` environment variable. | ||
| * @param options.appUrl The URL of the Braintrust App. Defaults to https://www.braintrust.dev. | ||
| * @param options.orgName (Optional) The name of a specific organization to connect to. | ||
| * @returns The created or updated environment association | ||
| * | ||
| * @example | ||
| * ```javascript | ||
| * // Associate prompt version "456" with the "production" environment | ||
| * const association = await setPromptEnvironment({ | ||
| * promptId: "prompt-uuid", | ||
| * environmentSlug: "production", | ||
| * version: "456", | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export async function setPromptEnvironment({ | ||
| promptId, | ||
| environmentSlug, | ||
| version, | ||
| appUrl, | ||
| apiKey, | ||
| orgName, | ||
| fetch, | ||
| forceLogin, | ||
| state: stateArg, | ||
| }: SetPromptEnvironmentOptions): Promise<PromptEnvironmentAssociation> { | ||
| const state = stateArg ?? _internalGetGlobalState(); | ||
|
|
||
| await state.login({ | ||
| orgName, | ||
| apiKey, | ||
| appUrl, | ||
| fetch, | ||
| forceLogin, | ||
| }); | ||
|
|
||
| // Use PUT for upsert behavior - need to use state.fetch since HTTPConnection doesn't have put method | ||
| const apiConn = state.apiConn(); | ||
| const url = `${apiConn.base_url}/environment-object/prompt/${promptId}/${environmentSlug}`; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we just fix this at the apiConn level? i.e. add a put_json() |
||
| const resp = await state.fetch(url, { | ||
| method: "PUT", | ||
| headers: { | ||
| "Content-Type": "application/json", | ||
| Accept: "application/json", | ||
| ...(apiConn.token ? { Authorization: `Bearer ${apiConn.token}` } : {}), | ||
| }, | ||
| body: JSON.stringify({ | ||
| object_version: version, | ||
| ...(orgName && { org_name: orgName }), | ||
| }), | ||
| }); | ||
|
|
||
| if (!resp.ok) { | ||
| const text = await resp.text(); | ||
| throw new Error(`${resp.status}: ${resp.statusText} (${text})`); | ||
| } | ||
|
|
||
| return (await resp.json()) as PromptEnvironmentAssociation; | ||
| } | ||
|
|
||
| /** | ||
| * Clear an environment association for a prompt. | ||
| * | ||
| * @param options Options for the request | ||
| * @param options.promptId The ID of the prompt | ||
| * @param options.environmentSlug The environment slug to delete (e.g., "production", "staging") | ||
| * @param options.apiKey The API key to use. If not specified, will use the `BRAINTRUST_API_KEY` environment variable. | ||
| * @param options.appUrl The URL of the Braintrust App. Defaults to https://www.braintrust.dev. | ||
| * @param options.orgName (Optional) The name of a specific organization to connect to. | ||
| * @returns The cleared environment association | ||
| * @throws If no association exists for the prompt and environment | ||
| * | ||
| * @example | ||
| * ```javascript | ||
| * // Remove the "staging" environment association | ||
| * await clearPromptEnvironment({ | ||
| * promptId: "prompt-uuid", | ||
| * environmentSlug: "staging", | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export async function clearPromptEnvironment({ | ||
| promptId, | ||
| environmentSlug, | ||
| appUrl, | ||
| apiKey, | ||
| orgName, | ||
| fetch, | ||
| forceLogin, | ||
| state: stateArg, | ||
| }: ClearPromptEnvironmentOptions): Promise<PromptEnvironmentAssociation> { | ||
| const state = stateArg ?? _internalGetGlobalState(); | ||
|
|
||
| await state.login({ | ||
| orgName, | ||
| apiKey, | ||
| appUrl, | ||
| fetch, | ||
| forceLogin, | ||
| }); | ||
|
|
||
| // Need to use state.fetch since HTTPConnection doesn't have delete method | ||
| const apiConn = state.apiConn(); | ||
| const queryString = orgName ? `?org_name=${encodeURIComponent(orgName)}` : ""; | ||
| const url = `${apiConn.base_url}/environment-object/prompt/${promptId}/${environmentSlug}${queryString}`; | ||
| const resp = await state.fetch(url, { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto here |
||
| method: "DELETE", | ||
| headers: { | ||
| Accept: "application/json", | ||
| ...(apiConn.token ? { Authorization: `Bearer ${apiConn.token}` } : {}), | ||
| }, | ||
| }); | ||
|
|
||
| if (!resp.ok) { | ||
| const text = await resp.text(); | ||
| throw new Error(`${resp.status}: ${resp.statusText} (${text})`); | ||
| } | ||
|
|
||
| return (await resp.json()) as PromptEnvironmentAssociation; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect an Environments class with a list, set, clear.
something like
prompt.environments.list()and so onThis would follow existing
prompt.projectetc.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd imagine this environment class would be used in logger instead of the function based approach