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 futureagi
npm 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

FieldHolds
nameThe template’s name
messagesOrdered SystemMessage / UserMessage / AssistantMessage objects, plus any placeholder entries
model_configurationA ModelConfig: model name and generation settings. See Model configuration for every field and its valid range
variable_namesSample values for each {{name}} the messages reference
placeholdersNamed 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.

StepPythonTypeScript
Create or open a draftPrompt(template=template).create()await new Prompt(template).open()
Save changes to the current draftclient.save_current_draft()await client.saveCurrentDraft()
Commit, optionally set default and a labelclient.commit_current_version(message="...", set_default=True, label="Production")await client.commitCurrentVersion("...", true, "Production")
Open a new draft versionclient.create_new_version(commit_message="...", set_default=True)await client.createNewVersion({ commit_message: "...", set_default: true })
Set an already-committed version as defaultPrompt.set_default_version(template_name="support-agent", version="v2")await Prompt.setDefaultVersion("support-agent", "v2")
Delete the templateclient.delete()await client.delete()
Delete the template by namePrompt.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 v2
const 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 version
const 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

Was this page helpful?

Questions & Discussion