> ## Documentation Index
> Fetch the complete documentation index at: https://futureagi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Prompt Workbench Using SDK

### **Installation**

<CodeGroup>
  ```bash npm theme={null}
  npm install @future-agi/sdk
  ```

  ```bash pip theme={null}
  pip install futureagi
  ```
</CodeGroup>

<Note>
  The Python package is installed as `futureagi` but imported as `fi`.
</Note>

***

### **Template structure**

#### **Basic components**

* **Name**: unique identifier (required)
* **Messages**: ordered list of messages
* **Model configuration**: model + generation params
* **Variables**: dynamic placeholders used in messages

#### **Message types**

* **System**: sets behavior/context
* **User**: contains the prompt; supports variables like `{{var}}`
* **Assistant**: few-shot examples or expected outputs

```json theme={null}
{ "role": "system", "content": "You are a helpful assistant." }
{ "role": "user", "content": "Introduce {{name}} from {{city}}." }
{ "role": "assistant", "content": "Meet Ada from Berlin!" }
```

***

### **Model configuration fields**

`model_name`, `temperature`, `frequency_penalty`, `presence_penalty`, `max_tokens`, `top_p`, `response_format`, `tool_choice`, `tools`

***

### **Placeholders and compile**

Add a placeholder message (`type="placeholder"`, `name="..."`) in your template. At compile time, supply an array of messages for that key; `{{var}}` variables are substituted in all message contents.

<CodeGroup>
  ```typescript JS/TS theme={null}
  import { PromptTemplate, ModelConfig, MessageBase, Prompt } from "@future-agi/sdk";

  const tpl = new PromptTemplate({
    name: "chat-template",
    messages: [
      { role: "system", content: "You are a helpful assistant." } as MessageBase,
      { role: "user", content: "Hello {{name}}!" } as MessageBase,
      { type: "placeholder", name: "history" } as any, // placeholder
    ],
    model_configuration: new ModelConfig({ model_name: "gpt-4o-mini" }),
  });

  const client = new Prompt(tpl);
  // Compile with substitution and inlined chat history
  const compiled = client.compile({
    name: "Alice",
    history: [{ role: "user", content: "Ping {{name}}" }],
  } as any);
  ```

  ```python Python theme={null}
  from fi.prompt import Prompt, PromptTemplate, ModelConfig, SystemMessage, UserMessage

  tpl = PromptTemplate(
      name="chat-template",
      messages=[
          SystemMessage(content="You are a helpful assistant."),
          UserMessage(content="Hello {{name}}!"),
          {"type": "placeholder", "name": "history"},
      ],
      model_configuration=ModelConfig(model_name="gpt-4o-mini"),
  )

  client = Prompt(template=tpl)
  compiled = client.compile(name="Alice", history=[{"role": "user", "content": "Ping {{name}}"}])
  ```
</CodeGroup>

***

### **Create templates**

<CodeGroup>
  ```typescript JS/TS theme={null}
  import { Prompt, PromptTemplate, ModelConfig, MessageBase } from "@future-agi/sdk";

  const tpl = new PromptTemplate({
    name: "intro-template",
    messages: [
      { role: "system", content: "You are a helpful assistant." } as MessageBase,
      { role: "user", content: "Introduce {{name}} from {{city}}." } as MessageBase,
    ],
    variable_names: { name: ["Ada"], city: ["Berlin"] },
    model_configuration: new ModelConfig({ model_name: "gpt-4o-mini" }),
  });

  const client = new Prompt(tpl);
  await client.open();                               // draft v1
  await client.commitCurrentVersion("Finish v1", true); // set default
  ```

  ```python Python theme={null}
  from fi.prompt import Prompt, PromptTemplate, ModelConfig, SystemMessage, UserMessage

  tpl = PromptTemplate(
      name="intro-template",
      messages=[
          SystemMessage(content="You are a helpful assistant."),
          UserMessage(content="Introduce {{name}} from {{city}}."),
      ],
      variable_names={"name": ["Ada"], "city": ["Berlin"]},
      model_configuration=ModelConfig(model_name="gpt-4o-mini"),
  )

  client = Prompt(template=tpl).create()                 # draft v1
  client.commit_current_version(message="Finish v1", set_default=True)
  ```
</CodeGroup>

***

### **Versioning (step-by-step)**

* Build the template (see above)
* Create draft v1 (JS/TS: `await client.open()`; Python: `client.create()`)
* Update draft & save (JS/TS: `saveCurrentDraft()`; Python: `save_current_draft()`)
* Commit v1 and set default (JS/TS: `commitCurrentVersion("msg", true)`; Python: `commit_current_version`)
* Open a new draft (JS/TS: `createNewVersion()`; Python: `create_new_version()`)
* Delete if needed (JS/TS: `delete()`; Python: `delete()`)

***

### **Labels (deployment control)**

* **System labels**: Production, Staging, Development (predefined by backend)
* **Custom labels**: create explicitly and assign to versions
* **Name-based APIs**: manage by names (no IDs needed)
* **Draft safety**: cannot assign labels to drafts; assignments are queued and applied on commit

#### **Assign labels**

<CodeGroup>
  ```typescript JS/TS theme={null}
  // Assign by instance (current project)
  await client.labels().assign("Production", "v1");
  await client.labels().assign("Staging", "v2");

  // Create and assign a custom label
  await client.labels().create("Canary");
  await client.labels().assign("Canary", "v2");

  // Class helpers by names (org-wide context)
  await Prompt.assignLabelToTemplateVersion("intro-template", "v2", "Development");
  ```

  ```python Python theme={null}
  # Assign by instance
  client.assign_label("Production", version="v1")
  client.assign_label("Staging", version="v2")

  # Create and assign a custom label
  client.create_label("Canary")
  client.assign_label("Canary", version="v2")

  # Class helpers by names
  Prompt.assign_label_to_template_version(template_name="intro-template", version="v2", label="Development")
  ```
</CodeGroup>

#### **Remove labels**

<CodeGroup>
  ```typescript JS/TS theme={null}
  await client.labels().remove("Canary", "v2");
  await Prompt.removeLabelFromTemplateVersion("intro-template", "v2", "Development");
  ```

  ```python Python theme={null}
  client.remove_label("Canary", version="v2")
  Prompt.remove_label_from_template_version(template_name="intro-template", version="v2", label="Development")
  ```
</CodeGroup>

#### **List labels and mappings**

<CodeGroup>
  ```typescript JS/TS theme={null}
  const labels = await client.labels().list(); // system + custom
  const mapping = await Prompt.getTemplateLabels({ template_name: "intro-template" });
  ```

  ```python Python theme={null}
  labels = client.list_labels()
  mapping = Prompt.get_template_labels(template_name="intro-template")
  ```
</CodeGroup>

***

### **Fetch by name + label (or version)**

<Note>
  <ul>
    <li><b>Precedence</b>: version > label</li>
    <li><b>Python default</b>: if no label is provided, defaults to <code>"production"</code></li>
    <li><b>Return type</b>: <code>get\_template\_by\_name()</code> returns a <code>Prompt</code> object (not a <code>PromptTemplate</code>). You can call <code>.compile()</code> directly on it.</li>
  </ul>
</Note>

<CodeGroup>
  ```typescript JS/TS theme={null}
  import { Prompt } from "@future-agi/sdk";
  const tplByLabel = await Prompt.getTemplateByName("intro-template", { label: "Production" });
  const tplByVersion = await Prompt.getTemplateByName("intro-template", { version: "v2" });
  ```

  ```python Python theme={null}
  from fi.prompt import Prompt
  tpl_by_label = Prompt.get_template_by_name("intro-template", label="Production")
  tpl_by_version = Prompt.get_template_by_name("intro-template", version="v2")
  ```
</CodeGroup>

***

### **A/B testing with labels (compile -> OpenAI gpt‑4o)**

Fetch two labeled versions of the same template (e.g., `prod-a` and `prod-b`), randomly select one, compile variables, and send the compiled messages to OpenAI.

<Note>
  The `compile()` API replaces `{{var}}` in string contents and preserves structured contents. Ensure your template contains the variables you pass (e.g., `{{name}}`, `{{city}}`).
</Note>

<CodeGroup>
  ```typescript JS/TS theme={null}
  import OpenAI from "openai";
  import { Prompt, PromptTemplate } from "@future-agi/sdk";

  const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });

  // Fetch both label variants
  const [tplA, tplB] = await Promise.all([
    Prompt.getTemplateByName("my-template-name", { label: "prod-a" }),
    Prompt.getTemplateByName("my-template-name", { label: "prod-b" }),
  ]);

  // Randomly select a variant
  const selected = Math.random() < 0.5 ? tplA : tplB;
  const client = new Prompt(selected as PromptTemplate);

  // Compile variables into the template messages
  const compiled = client.compile({ name: "Ada", city: "Berlin" });

  // Send to OpenAI gpt-4o
  const completion = await openai.chat.completions.create({
    model: "gpt-4o",
    messages: compiled as any,
  });

  const resultText = completion.choices[0]?.message?.content;
  ```

  ```python Python theme={null}
  import os
  import random
  from openai import OpenAI
  from fi.prompt import Prompt

  openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

  # Fetch both label variants
  tpl_a = Prompt.get_template_by_name("my-template-name", label="prod-a")
  tpl_b = Prompt.get_template_by_name("my-template-name", label="prod-b")

  # Randomly select a variant
  selected_tpl = tpl_a if random.random() < 0.5 else tpl_b
  client = Prompt(template=selected_tpl)

  # Compile variables into the template messages
  compiled = client.compile(name="Ada", city="Berlin")

  # Send to OpenAI gpt-4o
  response = openai_client.chat.completions.create(
      model="gpt-4o",
      messages=compiled,
  )
  result_text = response.choices[0].message.content
  ```
</CodeGroup>

<Note>
  For analytics, attach the selected label/version to your logs or tracing so A/B results can be compared.
</Note>

***

### **Compile output format**

The `compile()` method returns messages in a provider-agnostic format. Each message contains structured content that you may need to convert for your target LLM provider.

**Output structure:**

```json theme={null}
[
  {"role": "system", "content": "[{'text': 'You are a helpful assistant.', 'type': 'text'}]"},
  {"role": "user", "content": "[{'text': 'Hello Ada from Berlin!', 'type': 'text'}]"}
]
```

<Note>
  The `content` field contains a stringified list of content parts. This format supports multimodal content (text, images, etc.) and is intentionally provider-agnostic. Write an adapter function to convert to your target provider's format.
</Note>

#### **OpenAI adapter example**

<CodeGroup>
  ```typescript JS/TS theme={null}
  function toOpenAIFormat(compiled: any[]): { role: string; content: string }[] {
    return compiled.map((msg) => {
      let content = msg.content;
      try {
        // Parse the stringified content array
        const parsed = JSON.parse(content.replace(/'/g, '"'));
        if (Array.isArray(parsed)) {
          content = parsed.map((item: any) => item.text || "").join("");
        }
      } catch {
        // Already plain text
      }
      return { role: msg.role, content };
    });
  }

  const openaiMessages = toOpenAIFormat(compiled);
  ```

  ```python Python theme={null}
  import json

  def to_openai_format(compiled_messages):
      """Convert compiled messages to OpenAI format"""
      openai_msgs = []
      for msg in compiled_messages:
          role = msg.get("role", "user")
          content = msg.get("content", "")
          try:
              # Parse stringified content (Python dict syntax uses single quotes)
              content_list = json.loads(content.replace("'", '"'))
              if isinstance(content_list, list):
                  content = "".join(item.get("text", "") for item in content_list)
          except (json.JSONDecodeError, TypeError):
              pass  # Already plain text
          openai_msgs.append({"role": role, "content": content})
      return openai_msgs

  openai_messages = to_openai_format(compiled)
  ```
</CodeGroup>

***
