SDK & API
Prompt SDK calls for templates, versions, labels, and compile
Prompts from code
From code, you can build a prompt template, move it through drafts and versions, point labels at a version, fetch it by name, and compile it into messages ready for a model call. Execution support was removed from both SDKs: neither exposes a run method, so running a prompt or comparing versions stays in the editor. This page is the call reference for everything else.
Install and authenticate
pip install futureaginpm install @future-agi/sdk Note
The Python package is published as futureagi but imported as fi, for example from fi.prompt import Prompt.
Every call on this page needs your Future AGI credentials:
export FI_API_KEY="your-api-key"
export FI_SECRET_KEY="your-secret-key"
Find both under Settings → API Keys in the platform.
Construct a template
| Field | Holds |
|---|---|
name | The template’s name |
messages | Ordered SystemMessage / UserMessage / AssistantMessage objects, plus any placeholder entries |
model_configuration | A ModelConfig: model name and generation settings. See Model configuration for every field and its valid range |
variable_names | Sample values for each {{name}} the messages reference |
placeholders | Named slots that take a list of messages instead of a string, set as a {"type": "placeholder", "name": ...} entry in messages |
from fi.prompt import Prompt, PromptTemplate, ModelConfig, SystemMessage, UserMessage
template = PromptTemplate(
name="support-agent",
messages=[
SystemMessage(content="You are a support agent for {{company_name}}"),
{"type": "placeholder", "name": "history"},
UserMessage(content="{{customer_question}}"),
],
model_configuration=ModelConfig(model_name="gpt-4o-mini"),
variable_names={
"company_name": ["Acme"],
"customer_question": ["Where is my order?"],
},
)import { Prompt, PromptTemplate, ModelConfig, SystemMessage, UserMessage } from "@future-agi/sdk";
const template = new PromptTemplate({
name: "support-agent",
messages: [
new SystemMessage("You are a support agent for {{company_name}}"),
{ type: "placeholder", name: "history" } as any, // PromptTemplate.messages is typed MessageBase[], so a placeholder entry needs the cast
new UserMessage("{{customer_question}}"),
],
model_configuration: new ModelConfig({ model_name: "gpt-4o-mini" }),
variable_names: {
company_name: ["Acme"],
customer_question: ["Where is my order?"],
},
}); Version lifecycle
A template moves through the same states from code as it does in the editor: draft, commit, new draft.
| Step | Python | TypeScript |
|---|---|---|
| Create or open a draft | Prompt(template=template).create() | await new Prompt(template).open() |
| Save changes to the current draft | client.save_current_draft() | await client.saveCurrentDraft() |
| Commit, optionally set default and a label | client.commit_current_version(message="...", set_default=True, label="Production") | await client.commitCurrentVersion("...", true, "Production") |
| Open a new draft version | client.create_new_version(commit_message="...", set_default=True) | await client.createNewVersion({ commit_message: "...", set_default: true }) |
| Set an already-committed version as default | Prompt.set_default_version(template_name="support-agent", version="v2") | await Prompt.setDefaultVersion("support-agent", "v2") |
| Delete the template | client.delete() | await client.delete() |
| Delete the template by name | Prompt.delete_template_by_name("support-agent") | await Prompt.deleteTemplateByName("support-agent") |
Wherever a call takes a version, pass it as v followed by the version number, for example "v1" or "v2". Any other shape is rejected.
Note
In Python, constructing Prompt(template=template) looks the template up by name first. In TypeScript, new Prompt(template) does no lookup: it only assigns the template, and the name lookup happens inside open(), which adopts the existing template and returns the client rather than raising. In Python, calling create() on a name that already exists raises TemplateAlreadyExists; use get_template_by_name() to open an existing one instead.
client = Prompt(template=template)
client.create() # draft v1
client.save_current_draft() # push further edits to the v1 draft
client.commit_current_version(
message="Add escalation instructions",
set_default=True,
label="Production",
)
client.create_new_version(
commit_message="Tune temperature",
set_default=False,
) # commits v1 if still a draft, then opens v2const client = new Prompt(template);
await client.open(); // draft v1
await client.saveCurrentDraft(); // push further edits to the v1 draft
await client.commitCurrentVersion("Add escalation instructions", true, "Production");
await client.createNewVersion({
commit_message: "Tune temperature",
set_default: false,
}); // commits v1 if still a draft, then opens v2 Note
save_current_draft() / saveCurrentDraft() only work on a draft. Called against a version that’s already committed, both raise: create a new draft version first.
Labels
Three system labels are available to every template: Production, Staging, and Development. A custom label works the same way once you create it.
client.create_label("Canary")
client.assign_label("Canary", version="v2")
client.remove_label("Canary", version="v2")
labels = client.list_labels()await client.labels().create("Canary");
await client.labels().assign("Canary", "v2");
await client.labels().remove("Canary", "v2");
const labels = await client.labels().list(); Note
Assigning a label to the version the client currently has open doesn’t fail even while that version is still a draft: assign_label() / labels().assign() queue the assignment and apply it automatically on your next commit. Pass any other version and the assignment applies immediately instead of queueing.
The name-based class helpers skip loading a template instance first; they resolve everything by name, including the version:
Prompt.assign_label_to_template_version(template_name="support-agent", version="v2", label="Development")
Prompt.remove_label_from_template_version(template_name="support-agent", version="v2", label="Development")
Prompt.get_template_labels(template_name="support-agent")await Prompt.assignLabelToTemplateVersion("support-agent", "v2", "Development");
await Prompt.removeLabelFromTemplateVersion("support-agent", "v2", "Development");
await Prompt.getTemplateLabels({ template_name: "support-agent" }); Note
Only assign_label_to_template_version() / assignLabelToTemplateVersion() checks this: pointing it at a version that’s still a draft raises an error telling you to commit first, instead of queueing like assign_label() does. remove_label_from_template_version() / removeLabelFromTemplateVersion() and get_template_labels() / getTemplateLabels() don’t check at all.
Fetch by name
An explicit version wins over an explicit label. Pass neither, and both SDKs fall back to whatever the Production label currently points at; if nothing carries that label yet, they fall back again to the template’s default version.
Note
Return type differs. Python’s get_template_by_name() returns a Prompt instance, so you can call .compile() on it directly. TypeScript’s getTemplateByName() returns a raw PromptTemplate; wrap it in new Prompt(tpl) before calling .compile().
by_version = Prompt.get_template_by_name("support-agent", version="v2")
by_label = Prompt.get_template_by_name("support-agent", label="Staging")
by_default = Prompt.get_template_by_name("support-agent") # Production, then the default versionconst byVersion = await Prompt.getTemplateByName("support-agent", { version: "v2" });
const byLabelTpl = await Prompt.getTemplateByName("support-agent", { label: "Staging" });
const byLabel = new Prompt(byLabelTpl); // wrap: getTemplateByName returns a PromptTemplate, not a Prompt
const byDefaultTpl = await Prompt.getTemplateByName("support-agent"); // Production, then the default version
const byDefault = new Prompt(byDefaultTpl); Compile
compile() substitutes each {{name}} in the message content with the value you pass, and expands a placeholder entry into the list of messages you supply for it.
compiled = client.compile(
company_name="Acme",
customer_question="Where is my order?",
history=[{"role": "user", "content": "I ordered a jacket yesterday."}],
)const compiled = client.compile({
company_name: "Acme",
customer_question: "Where is my order?",
history: [{ role: "user", content: "I ordered a jacket yesterday." }],
} as any); Both return a flat list of {role, content} messages, the placeholder’s messages inlined at the position it occupied in the template:
[
{ "role": "system", "content": "You are a support agent for Acme" },
{ "role": "user", "content": "I ordered a jacket yesterday." },
{ "role": "user", "content": "Where is my order?" }
]
Note
A history item missing role or content raises a ValueError in Python naming the placeholder. And the two SDKs shape content differently: Python’s compile() always returns a string, so structured or multimodal content gets stringified rather than preserved; TypeScript’s compile() keeps structured content as a list of parts and substitutes only the text fields.
Keep exploring
Questions & Discussion