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

# Migrate an existing prompt

> Move a prompt that already lives in your code (an inline SDK call, a Vercel AI SDK message, a LangChain ChatPromptTemplate, a LlamaIndex PromptTemplate) into a versioned AgentMark .prompt.mdx, and rewire the call site to load it. Keep your own SDK and model call.

You already have a prompt. It's just living in your application code: an inline `messages` array, a `system` string, a `ChatPromptTemplate`, a `PromptTemplate`. Migrating it to AgentMark means moving that text into a versioned [`.prompt.mdx`](/build/creating-prompts) and loading it at the call site, so the prompt is editable, type-safe, and traceable instead of hard-coded.

## The pattern

It's the same three moves regardless of which SDK or framework the prompt currently lives in:

1. **Extract** the prompt into `agentmark/<name>.prompt.mdx`. The `system` / `user` / `assistant` turns become message tags, and the template variables become `{props.*}`. See [Creating prompts](/build/creating-prompts) for the shape.
2. **Load + render** it at the call site: `client.loadTextPrompt(...)` then `prompt.format({ props })` gives you back `messages` (and `text_config`).
3. **Keep your model call.** You are replacing *where the prompt comes from*, not your SDK. Feed the rendered `messages` to whatever you already call (`generateText`, `openai.chat.completions.create`, `model.invoke`, …), then delete the inline prompt.

The prompt file is the same in every case. For the examples below:

```mdx agentmark/summarize.prompt.mdx theme={null}
---
name: summarize
text_config:
  model_name: openai/gpt-4o-mini
---

<System>You are a concise summarizer. Reply with a single sentence, no preamble.</System>
<User>Summarize this article:

{props.article}</User>
```

## Raw OpenAI / Anthropic SDK

An inline `messages` array (or `system` + `messages`) moves straight into the prompt file; the `create` call stays.

<CodeGroup>
  ```ts Before theme={null}
  import OpenAI from "openai";
  const openai = new OpenAI();

  export async function summarize(article: string) {
    const res = await openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages: [
        { role: "system", content: "You are a concise summarizer. Reply with a single sentence, no preamble." },
        { role: "user", content: `Summarize this article:\n\n${article}` },
      ],
    });
    return res.choices[0]?.message?.content ?? "";
  }
  ```

  ```ts After theme={null}
  import OpenAI from "openai";
  import { client } from "./agentmark.client";
  const openai = new OpenAI();

  export async function summarize(article: string) {
    const prompt = await client.loadTextPrompt("summarize.prompt.mdx");
    const { messages, text_config } = await prompt.format({ props: { article } });

    const res = await openai.chat.completions.create({
      model: text_config.model_name.replace(/^openai\//, ""),
      // Cast the neutral messages to OpenAI's param type. Structurally
      // compatible, but TypeScript won't infer it, so the call needs the cast.
      messages: messages as OpenAI.Chat.ChatCompletionMessageParam[],
    });
    return res.choices[0]?.message?.content ?? "";
  }
  ```
</CodeGroup>

The Anthropic SDK is the same idea: load + `format`, then pass `messages` (and split the `system` turn out as Anthropic requires) to `anthropic.messages.create`.

## Vercel AI SDK

`generateText`'s `system` + `prompt`/`messages` come from the rendered prompt; the `generateText` call stays.

<CodeGroup>
  ```ts Before theme={null}
  import { generateText } from "ai";
  import { openai } from "@ai-sdk/openai";

  export async function reply(question: string) {
    const { text } = await generateText({
      model: openai("gpt-4o-mini"),
      system: "You are a friendly support agent. Answer in two sentences or fewer.",
      prompt: question,
    });
    return text;
  }
  ```

  ```ts After theme={null}
  import { generateText } from "ai";
  import { openai } from "@ai-sdk/openai";
  import { client } from "./agentmark.client";

  export async function reply(question: string) {
    const prompt = await client.loadTextPrompt("reply.prompt.mdx");
    const { messages, text_config } = await prompt.format({ props: { question } });

    const { text } = await generateText({
      model: openai(text_config.model_name.replace(/^openai\//, "")),
      messages,
    });
    return text;
  }
  ```
</CodeGroup>

## LangChain

A `ChatPromptTemplate` is a prompt, so migrate it. The template's messages move into the `.prompt.mdx`; you drop the `ChatPromptTemplate` + `.pipe()` and keep the chat model, because LangChain chat models accept a message array directly.

<CodeGroup>
  ```ts Before theme={null}
  import { ChatPromptTemplate } from "@langchain/core/prompts";
  import { ChatOpenAI } from "@langchain/openai";

  const prompt = ChatPromptTemplate.fromMessages([
    ["system", "You extract structured fields. Return JSON with keys: name, date, amount."],
    ["human", "{document}"],
  ]);
  const model = new ChatOpenAI({ model: "gpt-4o-mini" });

  export async function extract(document: string) {
    const chain = prompt.pipe(model);
    const res = await chain.invoke({ document });
    return typeof res.content === "string" ? res.content : JSON.stringify(res.content);
  }
  ```

  ```ts After theme={null}
  import { ChatOpenAI } from "@langchain/openai";
  import { client } from "./agentmark.client";

  const model = new ChatOpenAI({ model: "gpt-4o-mini" });

  export async function extract(document: string) {
    const prompt = await client.loadTextPrompt("extract.prompt.mdx");
    const { messages } = await prompt.format({ props: { document } });

    const res = await model.invoke(messages);
    return typeof res.content === "string" ? res.content : JSON.stringify(res.content);
  }
  ```
</CodeGroup>

## LlamaIndex (Python)

A `PromptTemplate` (or `ChatPromptTemplate`) moves into the `.prompt.mdx`; you render with AgentMark and call the LLM with the resulting messages.

<CodeGroup>
  ```python Before theme={null}
  from llama_index.core import PromptTemplate
  from llama_index.llms.openai import OpenAI

  tmpl = PromptTemplate(
      "You extract structured fields. Return JSON with keys: name, date, amount.\n\n{document}"
  )
  llm = OpenAI(model="gpt-4o-mini")

  def extract(document: str) -> str:
      return llm.complete(tmpl.format(document=document)).text
  ```

  ```python After theme={null}
  from llama_index.llms.openai import OpenAI
  from agentmark_client import client

  llm = OpenAI(model="gpt-4o-mini")

  async def extract(document: str) -> str:
      prompt = await client.load_text_prompt("extract.prompt.mdx")
      formatted = await prompt.format(props={"document": document})
      res = llm.chat(formatted.messages)
      return res.message.content
  ```
</CodeGroup>

## After you migrate

Your model call doesn't change: same SDK, same function signature, same behavior. All that moved is where the prompt comes from: editing the prompt is now a versioned, reviewable [`.prompt.mdx`](/build/creating-prompts) change instead of a code edit, and every run is [traceable](/observe/overview).

Run `agentmark doctor` to confirm the prompt parses, then [run it](/build/running-prompts) to confirm the call site still works.

<div className="mt-8 rounded-lg bg-blue-50 p-6 dark:bg-blue-900/30">
  <h3 className="font-semibold mb-3">Have questions?</h3>
  <p className="mb-4">Reach out any time:</p>

  <ul>
    <li>
      Email the team at <a href="mailto:hello@agentmark.co" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200">[hello@agentmark.co](mailto:hello@agentmark.co)</a> for support
    </li>

    <li>
      Schedule an <a href="https://cal.com/ryan-randall/enterprise" className="text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-200">Enterprise Demo</a> to learn about AgentMark's business solutions
    </li>
  </ul>
</div>
