mirror of
https://github.com/katanemo/plano.git
synced 2026-04-25 16:56:24 +02:00
467 lines
No EOL
56 KiB
HTML
Executable file
467 lines
No EOL
56 KiB
HTML
Executable file
<!DOCTYPE html>
|
||
|
||
<html :class="{ 'dark' : darkMode === true }" data-content_root="../" lang="en" x-data="{ darkMode: $persist(window.matchMedia('(prefers-color-scheme: dark)').matches), activeSection: ''}">
|
||
<head>
|
||
<script>
|
||
(function () {
|
||
// Set initial color scheme
|
||
if ((localStorage.getItem("_x_darkMode") === "true") || (window.matchMedia("(prefers-color-scheme: dark)").matches)) {
|
||
document.documentElement.classList.add("dark");
|
||
}
|
||
|
||
// Watch for media preference changes
|
||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
|
||
localStorage.setItem("_x_darkMode", event.matches);
|
||
document.documentElement.classList.toggle("dark", event.matches);
|
||
});
|
||
})();
|
||
</script>
|
||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||
<meta charset="utf-8"/>
|
||
<meta content="#ffffff" media="(prefers-color-scheme: light)" name="theme-color"/>
|
||
<meta content="#030711" media="(prefers-color-scheme: dark)" name="theme-color"/>
|
||
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||
<title>Conversational State | Plano Docs v0.4.8</title>
|
||
<meta content="Conversational State | Plano Docs v0.4.8" property="og:title"/>
|
||
<meta content="Conversational State | Plano Docs v0.4.8" name="twitter:title"/>
|
||
<link href="../_static/pygments.css?v=73db4dac" rel="stylesheet" type="text/css"/>
|
||
<link href="../_static/theme.css?v=73505e79" rel="stylesheet" type="text/css"/>
|
||
<link href="../_static/sphinx-design.min.css?v=95c83b7e" rel="stylesheet" type="text/css"/>
|
||
<link href="../_static/css/custom.css?v=2929376a" rel="stylesheet" type="text/css"/>
|
||
<link href="../_static/awesome-sphinx-design.css?v=c54898a4" rel="stylesheet" type="text/css"/>
|
||
<link href="./docs/guides/state.html" rel="canonical"/>
|
||
<link href="../_static/favicon.ico" rel="icon"/>
|
||
<link href="../search.html" rel="search" title="Search"/>
|
||
<link href="../resources/tech_overview/tech_overview.html" rel="next" title="Tech Overview"/>
|
||
<link href="prompt_guard.html" rel="prev" title="Guardrails"/>
|
||
</head>
|
||
<body :class="{ 'overflow-hidden': showSidebar }" class="min-h-screen font-sans antialiased bg-background text-foreground" x-data="{ showSidebar: false, showScrollTop: false }">
|
||
<div @click.self="showSidebar = false" class="fixed inset-0 z-50 overflow-hidden bg-background/80 backdrop-blur-sm md:hidden" x-cloak="" x-show="showSidebar"></div><div class="relative flex flex-col min-h-screen" id="page"><a class="absolute top-0 left-0 z-[100] block bg-background p-4 text-xl transition -translate-x-full opacity-0 focus:translate-x-0 focus:opacity-100" href="#content">
|
||
Skip to content
|
||
</a><header class="sticky top-0 z-40 w-full border-b shadow-xs border-border bg-background/90 backdrop-blur"><div class="container flex items-center h-14">
|
||
<div class="hidden mr-4 md:flex">
|
||
<a class="flex items-center mr-6" href="../index.html">
|
||
<img alt="Logo" class="mr-2 dark:invert" height="24" src="../_static/favicon.ico" width="24"/><span class="hidden font-bold sm:inline-block text-clip whitespace-nowrap">Plano Docs v0.4.8</span>
|
||
</a></div><button @click="showSidebar = true" class="inline-flex items-center justify-center h-10 px-0 py-2 mr-2 text-base font-medium transition-colors rounded-md hover:text-accent-foreground hover:bg-transparent md:hidden" type="button">
|
||
<svg aria-hidden="true" fill="currentColor" height="24" viewbox="0 96 960 960" width="24" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M152.587 825.087q-19.152 0-32.326-13.174t-13.174-32.326q0-19.152 13.174-32.326t32.326-13.174h440q19.152 0 32.326 13.174t13.174 32.326q0 19.152-13.174 32.326t-32.326 13.174h-440Zm0-203.587q-19.152 0-32.326-13.174T107.087 576q0-19.152 13.174-32.326t32.326-13.174h320q19.152 0 32.326 13.174T518.087 576q0 19.152-13.174 32.326T472.587 621.5h-320Zm0-203.587q-19.152 0-32.326-13.174t-13.174-32.326q0-19.152 13.174-32.326t32.326-13.174h440q19.152 0 32.326 13.174t13.174 32.326q0 19.152-13.174 32.326t-32.326 13.174h-440ZM708.913 576l112.174 112.174q12.674 12.674 12.674 31.826t-12.674 31.826Q808.413 764.5 789.261 764.5t-31.826-12.674l-144-144Q600 594.391 600 576t13.435-31.826l144-144q12.674-12.674 31.826-12.674t31.826 12.674q12.674 12.674 12.674 31.826t-12.674 31.826L708.913 576Z"></path>
|
||
</svg>
|
||
<span class="sr-only">Toggle navigation menu</span>
|
||
</button>
|
||
<div class="flex items-center justify-between flex-1 gap-2 sm:gap-4 md:justify-end">
|
||
<div class="flex-1 w-full md:w-auto md:flex-none"><form @keydown.k.window.meta="$refs.search.focus()" action="../search.html" class="relative flex items-center group" id="searchbox" method="get">
|
||
<input aria-label="Search the docs" class="inline-flex items-center font-medium transition-colors bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background border border-input hover:bg-accent focus:bg-accent hover:text-accent-foreground focus:text-accent-foreground hover:placeholder-accent-foreground py-2 px-4 relative h-9 w-full justify-start rounded-[0.5rem] text-sm text-muted-foreground sm:pr-12 md:w-40 lg:w-64" id="search-input" name="q" placeholder="Search ..." type="search" x-ref="search"/>
|
||
<kbd class="pointer-events-none absolute right-1.5 top-2 hidden h-5 select-none text-muted-foreground items-center gap-1 rounded border border-border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 sm:flex group-hover:bg-accent group-hover:text-accent-foreground">
|
||
<span class="text-xs">⌘</span>
|
||
K
|
||
</kbd>
|
||
</form>
|
||
</div>
|
||
<nav class="flex items-center gap-1">
|
||
<a href="https://github.com/katanemo/plano" rel="noopener nofollow" title="Visit repository on GitHub">
|
||
<div class="inline-flex items-center justify-center px-0 text-sm font-medium transition-colors rounded-md hover:bg-accent hover:text-accent-foreground h-9 w-9">
|
||
<svg fill="currentColor" height="26px" style="margin-top:-2px;display:inline" viewbox="0 0 45 44" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M22.477.927C10.485.927.76 10.65.76 22.647c0 9.596 6.223 17.736 14.853 20.608 1.087.2 1.483-.47 1.483-1.047 0-.516-.019-1.881-.03-3.693-6.04 1.312-7.315-2.912-7.315-2.912-.988-2.51-2.412-3.178-2.412-3.178-1.972-1.346.149-1.32.149-1.32 2.18.154 3.327 2.24 3.327 2.24 1.937 3.318 5.084 2.36 6.321 1.803.197-1.403.759-2.36 1.379-2.903-4.823-.548-9.894-2.412-9.894-10.734 0-2.37.847-4.31 2.236-5.828-.224-.55-.969-2.759.214-5.748 0 0 1.822-.584 5.972 2.226 1.732-.482 3.59-.722 5.437-.732 1.845.01 3.703.25 5.437.732 4.147-2.81 5.967-2.226 5.967-2.226 1.185 2.99.44 5.198.217 5.748 1.392 1.517 2.232 3.457 2.232 5.828 0 8.344-5.078 10.18-9.916 10.717.779.67 1.474 1.996 1.474 4.021 0 2.904-.027 5.247-.027 5.96 0 .58.392 1.256 1.493 1.044C37.981 40.375 44.2 32.24 44.2 22.647c0-11.996-9.726-21.72-21.722-21.72" fill="currentColor" fill-rule="evenodd"></path></svg>
|
||
</div>
|
||
</a>
|
||
<button @click="darkMode = !darkMode" class="relative inline-flex items-center justify-center px-0 text-sm font-medium transition-colors rounded-md hover:bg-accent hover:text-accent-foreground h-9 w-9" title="Toggle color scheme" type="button">
|
||
<svg class="absolute transition-all scale-100 rotate-0 dark:-rotate-90 dark:scale-0" fill="currentColor" height="16" viewbox="0 96 960 960" width="16" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M480 685q45.456 0 77.228-31.772Q589 621.456 589 576q0-45.456-31.772-77.228Q525.456 467 480 467q-45.456 0-77.228 31.772Q371 530.544 371 576q0 45.456 31.772 77.228Q434.544 685 480 685Zm0 91q-83 0-141.5-58.5T280 576q0-83 58.5-141.5T480 376q83 0 141.5 58.5T680 576q0 83-58.5 141.5T480 776ZM80 621.5q-19.152 0-32.326-13.174T34.5 576q0-19.152 13.174-32.326T80 530.5h80q19.152 0 32.326 13.174T205.5 576q0 19.152-13.174 32.326T160 621.5H80Zm720 0q-19.152 0-32.326-13.174T754.5 576q0-19.152 13.174-32.326T800 530.5h80q19.152 0 32.326 13.174T925.5 576q0 19.152-13.174 32.326T880 621.5h-80Zm-320-320q-19.152 0-32.326-13.174T434.5 256v-80q0-19.152 13.174-32.326T480 130.5q19.152 0 32.326 13.174T525.5 176v80q0 19.152-13.174 32.326T480 301.5Zm0 720q-19.152 0-32.326-13.17Q434.5 995.152 434.5 976v-80q0-19.152 13.174-32.326T480 850.5q19.152 0 32.326 13.174T525.5 896v80q0 19.152-13.174 32.33-13.174 13.17-32.326 13.17ZM222.174 382.065l-43-42Q165.5 327.391 166 308.239t13.174-33.065q13.435-13.674 32.587-13.674t32.065 13.674l42.239 43q12.674 13.435 12.555 31.706-.12 18.272-12.555 31.946-12.674 13.674-31.445 13.413-18.772-.261-32.446-13.174Zm494 494.761-42.239-43q-12.674-13.435-12.674-32.087t12.674-31.565Q686.609 756.5 705.38 757q18.772.5 32.446 13.174l43 41.761Q794.5 824.609 794 843.761t-13.174 33.065Q767.391 890.5 748.239 890.5t-32.065-13.674Zm-42-494.761Q660.5 369.391 661 350.62q.5-18.772 13.174-32.446l41.761-43Q728.609 261.5 747.761 262t33.065 13.174q13.674 13.435 13.674 32.587t-13.674 32.065l-43 42.239q-13.435 12.674-31.706 12.555-18.272-.12-31.946-12.555Zm-495 494.761Q165.5 863.391 165.5 844.239t13.674-32.065l43-42.239q13.435-12.674 32.087-12.674t31.565 12.674Q299.5 782.609 299 801.38q-.5 18.772-13.174 32.446l-41.761 43Q231.391 890.5 212.239 890t-33.065-13.174ZM480 576Z"></path>
|
||
</svg>
|
||
<svg class="absolute transition-all scale-0 rotate-90 dark:rotate-0 dark:scale-100" fill="currentColor" height="16" viewbox="0 96 960 960" width="16" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M480 936q-151 0-255.5-104.5T120 576q0-138 90-239.5T440 218q25-3 39 18t-1 44q-17 26-25.5 55t-8.5 61q0 90 63 153t153 63q31 0 61.5-9t54.5-25q21-14 43-1.5t19 39.5q-14 138-117.5 229T480 936Zm0-80q88 0 158-48.5T740 681q-20 5-40 8t-40 3q-123 0-209.5-86.5T364 396q0-20 3-40t8-40q-78 32-126.5 102T200 576q0 116 82 198t198 82Zm-10-270Z"></path>
|
||
</svg>
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
<div class="flex-1"><div class="container md:grid md:grid-cols-[220px_minmax(0,1fr)] md:gap-6 lg:grid-cols-[240px_minmax(0,1fr)] lg:gap-10"><aside :aria-hidden="!showSidebar" :class="{ 'translate-x-0': showSidebar }" class="fixed inset-y-0 left-0 md:top-14 z-50 md:z-30 bg-background md:bg-transparent transition-all duration-100 -translate-x-full md:translate-x-0 ml-0 p-6 md:p-0 md:-ml-2 md:h-[calc(100vh-3.5rem)] w-5/6 md:w-full overflow-y-auto border-r border-border md:sticky" id="left-sidebar">
|
||
<a class="justify-start text-sm md:!hidden bg-background" href="../index.html">
|
||
<img alt="Logo" class="mr-2 dark:invert" height="16" src="../_static/favicon.ico" width="16"/><span class="font-bold text-clip whitespace-nowrap">Plano Docs v0.4.8</span>
|
||
</a>
|
||
<div class="relative overflow-hidden md:overflow-auto my-4 md:my-0">
|
||
<div class="overflow-y-auto h-full w-full relative pr-6">
|
||
|
||
<script async="" src="https://www.googletagmanager.com/gtag/js?id=G-EH2VW19FXE"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag(){dataLayer.push(arguments);}
|
||
gtag('js', new Date());
|
||
|
||
gtag('config', 'G-EH2VW19FXE');
|
||
</script>
|
||
<nav class="table w-full min-w-full my-6 lg:my-8">
|
||
<p class="caption" role="heading"><span class="caption-text">Get Started</span></p>
|
||
<ul>
|
||
<li class="toctree-l1"><a class="reference internal" href="../get_started/overview.html">Overview</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../get_started/intro_to_plano.html">Intro to Plano</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../get_started/quickstart.html">Quickstart</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../get_started/quickstart.html#next-steps">Next Steps</a></li>
|
||
</ul>
|
||
<p class="caption" role="heading"><span class="caption-text">Concepts</span></p>
|
||
<ul>
|
||
<li class="toctree-l1"><a class="reference internal" href="../concepts/listeners.html">Listeners</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../concepts/agents.html">Agents</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../concepts/filter_chain.html">Filter Chains</a></li>
|
||
<li class="toctree-l1" x-data="{ expanded: $el.classList.contains('current') ? true : false }"><a :class="{ 'expanded' : expanded }" @click="expanded = !expanded" class="reference internal expandable" href="../concepts/llm_providers/llm_providers.html">Model (LLM) Providers<button @click.prevent.stop="expanded = !expanded" type="button" x-cloak=""><span class="sr-only"></span><svg fill="currentColor" height="18px" stroke="none" viewbox="0 0 24 24" width="18px" xmlns="http://www.w3.org/2000/svg"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></svg></button></a><ul x-cloak="" x-show="expanded">
|
||
<li class="toctree-l2"><a class="reference internal" href="../concepts/llm_providers/supported_providers.html">Supported Providers & Configuration</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="../concepts/llm_providers/client_libraries.html">Client Libraries</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="../concepts/llm_providers/model_aliases.html">Model Aliases</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../concepts/prompt_target.html">Prompt Target</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../concepts/signals.html">Signals™</a></li>
|
||
</ul>
|
||
<p class="caption" role="heading"><span class="caption-text">Guides</span></p>
|
||
<ul class="current">
|
||
<li class="toctree-l1"><a class="reference internal" href="orchestration.html">Orchestration</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="llm_router.html">LLM Routing</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="function_calling.html">Function Calling</a></li>
|
||
<li class="toctree-l1" x-data="{ expanded: $el.classList.contains('current') ? true : false }"><a :class="{ 'expanded' : expanded }" @click="expanded = !expanded" class="reference internal expandable" href="observability/observability.html">Observability<button @click.prevent.stop="expanded = !expanded" type="button" x-cloak=""><span class="sr-only"></span><svg fill="currentColor" height="18px" stroke="none" viewbox="0 0 24 24" width="18px" xmlns="http://www.w3.org/2000/svg"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></svg></button></a><ul x-cloak="" x-show="expanded">
|
||
<li class="toctree-l2"><a class="reference internal" href="observability/tracing.html">Tracing</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="observability/monitoring.html">Monitoring</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="observability/access_logging.html">Access Logging</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l1"><a class="reference internal" href="prompt_guard.html">Guardrails</a></li>
|
||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Conversational State</a></li>
|
||
</ul>
|
||
<p class="caption" role="heading"><span class="caption-text">Resources</span></p>
|
||
<ul>
|
||
<li class="toctree-l1" x-data="{ expanded: $el.classList.contains('current') ? true : false }"><a :class="{ 'expanded' : expanded }" @click="expanded = !expanded" class="reference internal expandable" href="../resources/tech_overview/tech_overview.html">Tech Overview<button @click.prevent.stop="expanded = !expanded" type="button" x-cloak=""><span class="sr-only"></span><svg fill="currentColor" height="18px" stroke="none" viewbox="0 0 24 24" width="18px" xmlns="http://www.w3.org/2000/svg"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path></svg></button></a><ul x-cloak="" x-show="expanded">
|
||
<li class="toctree-l2"><a class="reference internal" href="../resources/tech_overview/request_lifecycle.html">Request Lifecycle</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="../resources/tech_overview/model_serving.html">Bright Staff</a></li>
|
||
<li class="toctree-l2"><a class="reference internal" href="../resources/tech_overview/threading_model.html">Threading Model</a></li>
|
||
</ul>
|
||
</li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../resources/deployment.html">Deployment</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../resources/configuration_reference.html">Configuration Reference</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../resources/cli_reference.html">CLI Reference</a></li>
|
||
<li class="toctree-l1"><a class="reference internal" href="../resources/llms_txt.html">llms.txt</a></li>
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
<button @click="showSidebar = false" class="absolute md:hidden right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100" type="button">
|
||
<svg class="h-4 w-4" fill="currentColor" height="24" stroke="none" viewbox="0 96 960 960" width="24" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M480 632 284 828q-11 11-28 11t-28-11q-11-11-11-28t11-28l196-196-196-196q-11-11-11-28t11-28q11-11 28-11t28 11l196 196 196-196q11-11 28-11t28 11q11 11 11 28t-11 28L536 576l196 196q11 11 11 28t-11 28q-11 11-28 11t-28-11L480 632Z"></path>
|
||
</svg>
|
||
</button>
|
||
</aside>
|
||
<main class="relative py-6 lg:gap-10 lg:py-8 xl:grid xl:grid-cols-[1fr_300px]">
|
||
<div class="w-full min-w-0 mx-auto">
|
||
<nav aria-label="breadcrumbs" class="flex items-center mb-4 space-x-1 text-sm text-muted-foreground">
|
||
<a class="overflow-hidden text-ellipsis whitespace-nowrap hover:text-foreground" href="../index.html">
|
||
<span class="hidden md:inline">Plano Docs v0.4.8</span>
|
||
<svg aria-label="Home" class="md:hidden" fill="currentColor" height="18" stroke="none" viewbox="0 96 960 960" width="18" xmlns="http://www.w3.org/2000/svg">
|
||
<path d="M240 856h120V616h240v240h120V496L480 316 240 496v360Zm-80 80V456l320-240 320 240v480H520V696h-80v240H160Zm320-350Z"></path>
|
||
</svg>
|
||
</a>
|
||
<div class="mr-1">/</div><span aria-current="page" class="font-medium text-foreground overflow-hidden text-ellipsis whitespace-nowrap">Conversational State</span>
|
||
</nav>
|
||
<div id="content" role="main">
|
||
<section id="conversational-state">
|
||
<span id="managing-conversational-state"></span><h1>Conversational State<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#conversational-state"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h1>
|
||
<p>The OpenAI Responses API (<code class="docutils literal notranslate"><span class="pre">v1/responses</span></code>) is designed for multi-turn conversations where context needs to persist across requests. Plano provides a unified <code class="docutils literal notranslate"><span class="pre">v1/responses</span></code> API that works with <strong>any LLM provider</strong>—OpenAI, Anthropic, Azure OpenAI, DeepSeek, or any OpenAI-compatible provider—while automatically managing conversational state for you.</p>
|
||
<p>Unlike the traditional Chat Completions API where you manually manage conversation history by including all previous messages in each request, Plano handles state management behind the scenes. This means you can use the Responses API with any model provider, and Plano will persist conversation context across requests—making it ideal for building conversational agents that remember context without bloating every request with full message history.</p>
|
||
<section id="how-it-works">
|
||
<h2>How It Works<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#how-it-works" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#how-it-works'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<p>When a client calls the Responses API:</p>
|
||
<ol class="arabic simple">
|
||
<li><p><strong>First request</strong>: Plano generates a unique <code class="docutils literal notranslate"><span class="pre">resp_id</span></code> and stores the conversation state (messages, model, provider, timestamp).</p></li>
|
||
<li><p><strong>Subsequent requests</strong>: The client includes the <code class="docutils literal notranslate"><span class="pre">previous_resp_id</span></code> from the previous response. Plano retrieves the stored conversation state, merges it with the new input, and sends the combined context to the LLM.</p></li>
|
||
<li><p><strong>Response</strong>: The LLM sees the full conversation history without the client needing to resend all previous messages.</p></li>
|
||
</ol>
|
||
<p>This pattern dramatically reduces bandwidth and makes it easier to build multi-turn agents—Plano handles the state plumbing so you can focus on agent logic.</p>
|
||
<p><strong>Example Using OpenAI Python SDK:</strong></p>
|
||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="kn">from</span><span class="w"> </span><span class="nn">openai</span><span class="w"> </span><span class="kn">import</span> <span class="n">OpenAI</span>
|
||
</span><span id="line-2">
|
||
</span><span id="line-3"><span class="c1"># Point to Plano's Model Proxy endpoint</span>
|
||
</span><span id="line-4"><span class="n">client</span> <span class="o">=</span> <span class="n">OpenAI</span><span class="p">(</span>
|
||
</span><span id="line-5"> <span class="n">api_key</span><span class="o">=</span><span class="s2">"test-key"</span><span class="p">,</span>
|
||
</span><span id="line-6"> <span class="n">base_url</span><span class="o">=</span><span class="s2">"http://127.0.0.1:12000/v1"</span>
|
||
</span><span id="line-7"><span class="p">)</span>
|
||
</span><span id="line-8">
|
||
</span><span id="line-9"><span class="c1"># First turn - Plano creates a new conversation state</span>
|
||
</span><span id="line-10"><span class="n">response</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">responses</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
|
||
</span><span id="line-11"> <span class="n">model</span><span class="o">=</span><span class="s2">"claude-sonnet-4-5"</span><span class="p">,</span> <span class="c1"># Works with any configured provider</span>
|
||
</span><span id="line-12"> <span class="nb">input</span><span class="o">=</span><span class="s2">"My name is Alice and I like Python"</span>
|
||
</span><span id="line-13"><span class="p">)</span>
|
||
</span><span id="line-14">
|
||
</span><span id="line-15"><span class="c1"># Save the response_id for conversation continuity</span>
|
||
</span><span id="line-16"><span class="n">resp_id</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">id</span>
|
||
</span><span id="line-17"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Assistant: </span><span class="si">{</span><span class="n">response</span><span class="o">.</span><span class="n">output_text</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</span><span id="line-18">
|
||
</span><span id="line-19"><span class="c1"># Second turn - Plano automatically retrieves previous context</span>
|
||
</span><span id="line-20"><span class="n">resp2</span> <span class="o">=</span> <span class="n">client</span><span class="o">.</span><span class="n">responses</span><span class="o">.</span><span class="n">create</span><span class="p">(</span>
|
||
</span><span id="line-21"> <span class="n">model</span><span class="o">=</span><span class="s2">"claude-sonnet-4-5"</span><span class="p">,</span> <span class="c1"># Make sure its configured in plano_config.yaml</span>
|
||
</span><span id="line-22"> <span class="nb">input</span><span class="o">=</span><span class="s2">"Please list all the messages you have received in our conversation, numbering each one."</span><span class="p">,</span>
|
||
</span><span id="line-23"> <span class="n">previous_response_id</span><span class="o">=</span><span class="n">resp_id</span><span class="p">,</span>
|
||
</span><span id="line-24"><span class="p">)</span>
|
||
</span><span id="line-25">
|
||
</span><span id="line-26"><span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Assistant: </span><span class="si">{</span><span class="n">resp2</span><span class="o">.</span><span class="n">output_text</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||
</span><span id="line-27"><span class="c1"># Output: "Your name is Alice and your favorite language is Python"</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p>Notice how the second request only includes the new user message—Plano automatically merges it with the stored conversation history before sending to the LLM.</p>
|
||
</section>
|
||
<section id="configuration-overview">
|
||
<h2>Configuration Overview<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#configuration-overview" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#configuration-overview'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<p>State storage is configured in the <code class="docutils literal notranslate"><span class="pre">state_storage</span></code> section of your <code class="docutils literal notranslate"><span class="pre">plano_config.yaml</span></code>:</p>
|
||
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="linenos"> 1</span><span class="nt">state_storage</span><span class="p">:</span>
|
||
</span><span id="line-2"><span class="linenos"> 2</span><span class="w"> </span><span class="c1"># Type: memory | postgres</span>
|
||
</span><span id="line-3"><mark><span class="linenos"> 3</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">postgres</span>
|
||
</mark></span><span id="line-4"><span class="linenos"> 4</span>
|
||
</span><span id="line-5"><span class="linenos"> 5</span><span class="w"> </span><span class="c1"># Connection string for postgres type</span>
|
||
</span><span id="line-6"><mark><span class="linenos"> 6</span><span class="w"> </span><span class="c1"># Environment variables are supported using $VAR_NAME or ${VAR_NAME} syntax</span>
|
||
</mark></span><span id="line-7"><mark><span class="linenos"> 7</span><span class="w"> </span><span class="c1"># Replace [USER] and [HOST] with your actual database credentials</span>
|
||
</mark></span><span id="line-8"><mark><span class="linenos"> 8</span><span class="w"> </span><span class="c1"># Variables like $DB_PASSWORD MUST be set before running config validation/rendering</span>
|
||
</mark></span><span id="line-9"><mark><span class="linenos"> 9</span><span class="w"> </span><span class="c1"># Example: Replace [USER] with 'myuser' and [HOST] with 'db.example.com:5432'</span>
|
||
</mark></span><span id="line-10"><mark><span class="linenos">10</span><span class="w"> </span><span class="nt">connection_string</span><span class="p">:</span><span class="w"> </span><span class="s">"postgresql://[USER]:$DB_PASSWORD@[HOST]:5432/postgres"</span>
|
||
</mark></span></code></pre></div>
|
||
</div>
|
||
<p>Plano supports two storage backends:</p>
|
||
<ul class="simple">
|
||
<li><p><strong>Memory</strong>: Fast, ephemeral storage for development and testing. State is lost when Plano restarts.</p></li>
|
||
<li><p><strong>PostgreSQL</strong>: Durable, production-ready storage with support for Supabase and self-hosted PostgreSQL instances.</p></li>
|
||
</ul>
|
||
<div class="admonition note">
|
||
<p class="admonition-title">Note</p>
|
||
<p>If you don’t configure <code class="docutils literal notranslate"><span class="pre">state_storage</span></code>, conversation state management is <strong>disabled</strong>. The Responses API will still work, but clients must manually include full conversation history in each request (similar to the Chat Completions API behavior).</p>
|
||
</div>
|
||
</section>
|
||
<section id="memory-storage-development">
|
||
<h2>Memory Storage (Development)<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#memory-storage-development" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#memory-storage-development'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<p>Memory storage keeps conversation state in-memory using a thread-safe <code class="docutils literal notranslate"><span class="pre">HashMap</span></code>. It’s perfect for local development, demos, and testing, but all state is lost when Plano restarts.</p>
|
||
<p><strong>Configuration</strong></p>
|
||
<p>Add this to your <code class="docutils literal notranslate"><span class="pre">plano_config.yaml</span></code>:</p>
|
||
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="nt">state_storage</span><span class="p">:</span>
|
||
</span><span id="line-2"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">memory</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p>That’s it. No additional setup required.</p>
|
||
<p><strong>When to Use Memory Storage</strong></p>
|
||
<ul class="simple">
|
||
<li><p>Local development and debugging</p></li>
|
||
<li><p>Demos and proof-of-concepts</p></li>
|
||
<li><p>Automated testing environments</p></li>
|
||
<li><p>Single-instance deployments where persistence isn’t critical</p></li>
|
||
</ul>
|
||
<p><strong>Limitations</strong></p>
|
||
<ul class="simple">
|
||
<li><p>State is lost on restart</p></li>
|
||
<li><p>Not suitable for production workloads</p></li>
|
||
<li><p>Cannot scale across multiple Plano instances</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="postgresql-storage-production">
|
||
<h2>PostgreSQL Storage (Production)<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#postgresql-storage-production" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#postgresql-storage-production'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<p>PostgreSQL storage provides durable, production-grade conversation state management. It works with both self-hosted PostgreSQL and Supabase (PostgreSQL-as-a-service), making it ideal for scaling multi-agent systems in production.</p>
|
||
<section id="prerequisites">
|
||
<h3>Prerequisites<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#prerequisites" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#prerequisites'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h3>
|
||
<p>Before configuring PostgreSQL storage, you need:</p>
|
||
<ol class="arabic simple">
|
||
<li><p>A PostgreSQL database (version 12 or later)</p></li>
|
||
<li><p>Database credentials (host, user, password)</p></li>
|
||
<li><p>The <code class="docutils literal notranslate"><span class="pre">conversation_states</span></code> table created in your database</p></li>
|
||
</ol>
|
||
<p><strong>Setting Up the Database</strong></p>
|
||
<p>Run the SQL schema to create the required table:</p>
|
||
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="linenos"> 1</span><span class="c1">-- Conversation State Storage Table</span>
|
||
</span><span id="line-2"><span class="linenos"> 2</span><span class="c1">-- This table stores conversational context for the OpenAI Responses API</span>
|
||
</span><span id="line-3"><span class="linenos"> 3</span><span class="c1">-- Run this SQL against your PostgreSQL/Supabase database before enabling conversation state storage</span>
|
||
</span><span id="line-4"><span class="linenos"> 4</span>
|
||
</span><span id="line-5"><span class="linenos"> 5</span><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">conversation_states</span><span class="w"> </span><span class="p">(</span>
|
||
</span><span id="line-6"><span class="linenos"> 6</span><span class="w"> </span><span class="n">response_id</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span>
|
||
</span><span id="line-7"><span class="linenos"> 7</span><span class="w"> </span><span class="n">input_items</span><span class="w"> </span><span class="n">JSONB</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
|
||
</span><span id="line-8"><span class="linenos"> 8</span><span class="w"> </span><span class="n">created_at</span><span class="w"> </span><span class="nb">BIGINT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
|
||
</span><span id="line-9"><span class="linenos"> 9</span><span class="w"> </span><span class="n">model</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
|
||
</span><span id="line-10"><span class="linenos">10</span><span class="w"> </span><span class="n">provider</span><span class="w"> </span><span class="nb">TEXT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span>
|
||
</span><span id="line-11"><span class="linenos">11</span><span class="w"> </span><span class="n">updated_at</span><span class="w"> </span><span class="k">TIMESTAMP</span><span class="w"> </span><span class="k">DEFAULT</span><span class="w"> </span><span class="k">CURRENT_TIMESTAMP</span>
|
||
</span><span id="line-12"><span class="linenos">12</span><span class="p">);</span>
|
||
</span><span id="line-13"><span class="linenos">13</span>
|
||
</span><span id="line-14"><span class="linenos">14</span><span class="c1">-- Indexes for common query patterns</span>
|
||
</span><span id="line-15"><span class="linenos">15</span><span class="k">CREATE</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">idx_conversation_states_created_at</span>
|
||
</span><span id="line-16"><span class="linenos">16</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">(</span><span class="n">created_at</span><span class="p">);</span>
|
||
</span><span id="line-17"><span class="linenos">17</span>
|
||
</span><span id="line-18"><span class="linenos">18</span><span class="k">CREATE</span><span class="w"> </span><span class="k">INDEX</span><span class="w"> </span><span class="k">IF</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">EXISTS</span><span class="w"> </span><span class="n">idx_conversation_states_provider</span>
|
||
</span><span id="line-19"><span class="linenos">19</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">(</span><span class="n">provider</span><span class="p">);</span>
|
||
</span><span id="line-20"><span class="linenos">20</span>
|
||
</span><span id="line-21"><span class="linenos">21</span><span class="c1">-- Optional: Add a policy for automatic cleanup of old conversations</span>
|
||
</span><span id="line-22"><span class="linenos">22</span><span class="c1">-- Uncomment and adjust the retention period as needed</span>
|
||
</span><span id="line-23"><span class="linenos">23</span><span class="c1">-- CREATE INDEX IF NOT EXISTS idx_conversation_states_updated_at</span>
|
||
</span><span id="line-24"><span class="linenos">24</span><span class="c1">-- ON conversation_states(updated_at);</span>
|
||
</span><span id="line-25"><span class="linenos">25</span>
|
||
</span><span id="line-26"><span class="linenos">26</span><span class="k">COMMENT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">conversation_states</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="s1">'Stores conversation history for OpenAI Responses API continuity'</span><span class="p">;</span>
|
||
</span><span id="line-27"><span class="linenos">27</span><span class="k">COMMENT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">.</span><span class="n">response_id</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="s1">'Unique identifier for the conversation state'</span><span class="p">;</span>
|
||
</span><span id="line-28"><span class="linenos">28</span><span class="k">COMMENT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">.</span><span class="n">input_items</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="s1">'JSONB array of conversation messages and context'</span><span class="p">;</span>
|
||
</span><span id="line-29"><span class="linenos">29</span><span class="k">COMMENT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">.</span><span class="n">created_at</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="s1">'Unix timestamp (seconds) when the conversation started'</span><span class="p">;</span>
|
||
</span><span id="line-30"><span class="linenos">30</span><span class="k">COMMENT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">.</span><span class="n">model</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="s1">'Model name used for this conversation'</span><span class="p">;</span>
|
||
</span><span id="line-31"><span class="linenos">31</span><span class="k">COMMENT</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="k">COLUMN</span><span class="w"> </span><span class="n">conversation_states</span><span class="p">.</span><span class="n">provider</span><span class="w"> </span><span class="k">IS</span><span class="w"> </span><span class="s1">'LLM provider (e.g., openai, anthropic, bedrock)'</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p><strong>Using psql:</strong></p>
|
||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><code><span id="line-1">psql<span class="w"> </span><span class="nv">$DATABASE_URL</span><span class="w"> </span>-f<span class="w"> </span>docs/db_setup/conversation_states.sql
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p><strong>Using Supabase Dashboard:</strong></p>
|
||
<ol class="arabic simple">
|
||
<li><p>Log in to your Supabase project</p></li>
|
||
<li><p>Navigate to the SQL Editor</p></li>
|
||
<li><p>Copy and paste the SQL from <code class="docutils literal notranslate"><span class="pre">docs/db_setup/conversation_states.sql</span></code></p></li>
|
||
<li><p>Run the query</p></li>
|
||
</ol>
|
||
</section>
|
||
<section id="configuration">
|
||
<h3>Configuration<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#configuration" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#configuration'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h3>
|
||
<p>Once the database table is created, configure Plano to use PostgreSQL storage:</p>
|
||
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="nt">state_storage</span><span class="p">:</span>
|
||
</span><span id="line-2"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">postgres</span>
|
||
</span><span id="line-3"><span class="w"> </span><span class="nt">connection_string</span><span class="p">:</span><span class="w"> </span><span class="s">"postgresql://user:password@host:5432/database"</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p><strong>Using Environment Variables</strong></p>
|
||
<p>You should <strong>never</strong> hardcode credentials. Use environment variables instead:</p>
|
||
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="nt">state_storage</span><span class="p">:</span>
|
||
</span><span id="line-2"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">postgres</span>
|
||
</span><span id="line-3"><span class="w"> </span><span class="nt">connection_string</span><span class="p">:</span><span class="w"> </span><span class="s">"postgresql://myuser:$DB_PASSWORD@db.example.com:5432/postgres"</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p>Then set the environment variable before running Plano:</p>
|
||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="nb">export</span><span class="w"> </span><span class="nv">DB_PASSWORD</span><span class="o">=</span><span class="s2">"your-secure-password"</span>
|
||
</span><span id="line-2"><span class="c1"># Run Plano or config validation</span>
|
||
</span><span id="line-3">./plano
|
||
</span></code></pre></div>
|
||
</div>
|
||
<div class="admonition warning">
|
||
<p class="admonition-title">Warning</p>
|
||
<p><strong>Special Characters in Passwords</strong>: If your password contains special characters like <code class="docutils literal notranslate"><span class="pre">#</span></code>, <code class="docutils literal notranslate"><span class="pre">@</span></code>, or <code class="docutils literal notranslate"><span class="pre">&</span></code>, you must URL-encode them in the connection string. For example, <code class="docutils literal notranslate"><span class="pre">MyPass#123</span></code> becomes <code class="docutils literal notranslate"><span class="pre">MyPass%23123</span></code>.</p>
|
||
</div>
|
||
</section>
|
||
<section id="supabase-connection-strings">
|
||
<h3>Supabase Connection Strings<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#supabase-connection-strings" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#supabase-connection-strings'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h3>
|
||
<p>Supabase requires different connection strings depending on your network setup. Most users should use the <strong>Session Pooler</strong> connection string.</p>
|
||
<p><strong>IPv4 Networks (Most Common)</strong></p>
|
||
<p>Use the Session Pooler connection string (port 5432):</p>
|
||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span><code><span id="line-1">postgresql://postgres.[PROJECT-REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p><strong>IPv6 Networks</strong></p>
|
||
<p>Use the direct connection (port 5432):</p>
|
||
<div class="highlight-text notranslate"><div class="highlight"><pre><span></span><code><span id="line-1">postgresql://postgres:[PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p><strong>Finding Your Connection String</strong></p>
|
||
<ol class="arabic simple">
|
||
<li><p>Go to your Supabase project dashboard</p></li>
|
||
<li><p>Navigate to <strong>Settings → Database → Connection Pooling</strong></p></li>
|
||
<li><p>Copy the <strong>Session mode</strong> connection string</p></li>
|
||
<li><p>Replace <code class="docutils literal notranslate"><span class="pre">[YOUR-PASSWORD]</span></code> with your actual database password</p></li>
|
||
<li><p>URL-encode special characters in the password</p></li>
|
||
</ol>
|
||
<p><strong>Example Configuration</strong></p>
|
||
<div class="highlight-yaml notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="nt">state_storage</span><span class="p">:</span>
|
||
</span><span id="line-2"><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">postgres</span>
|
||
</span><span id="line-3"><span class="w"> </span><span class="nt">connection_string</span><span class="p">:</span><span class="w"> </span><span class="s">"postgresql://postgres.myproject:$DB_PASSWORD@aws-0-us-west-2.pooler.supabase.com:5432/postgres"</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p>Then set the environment variable:</p>
|
||
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="c1"># If your password is "MyPass#123", encode it as "MyPass%23123"</span>
|
||
</span><span id="line-2"><span class="nb">export</span><span class="w"> </span><span class="nv">DB_PASSWORD</span><span class="o">=</span><span class="s2">"MyPass%23123"</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
</section>
|
||
</section>
|
||
<section id="troubleshooting">
|
||
<h2>Troubleshooting<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#troubleshooting" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#troubleshooting'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<p><strong>“Table ‘conversation_states’ does not exist”</strong></p>
|
||
<p>Run the SQL schema from <code class="docutils literal notranslate"><span class="pre">docs/db_setup/conversation_states.sql</span></code> against your database.</p>
|
||
<p><strong>Connection errors with Supabase</strong></p>
|
||
<ul class="simple">
|
||
<li><p>Verify you’re using the correct connection string format (Session Pooler for IPv4)</p></li>
|
||
<li><p>Check that your password is URL-encoded if it contains special characters</p></li>
|
||
<li><p>Ensure your Supabase project hasn’t paused due to inactivity (free tier)</p></li>
|
||
</ul>
|
||
<p><strong>Permission errors</strong></p>
|
||
<p>Ensure your database user has the following permissions:</p>
|
||
<div class="highlight-sql notranslate"><div class="highlight"><pre><span></span><code><span id="line-1"><span class="k">GRANT</span><span class="w"> </span><span class="k">SELECT</span><span class="p">,</span><span class="w"> </span><span class="k">INSERT</span><span class="p">,</span><span class="w"> </span><span class="k">UPDATE</span><span class="p">,</span><span class="w"> </span><span class="k">DELETE</span><span class="w"> </span><span class="k">ON</span><span class="w"> </span><span class="n">conversation_states</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="n">your_user</span><span class="p">;</span>
|
||
</span></code></pre></div>
|
||
</div>
|
||
<p><strong>State not persisting across requests</strong></p>
|
||
<ul class="simple">
|
||
<li><p>Verify <code class="docutils literal notranslate"><span class="pre">state_storage</span></code> is configured in your <code class="docutils literal notranslate"><span class="pre">plano_config.yaml</span></code></p></li>
|
||
<li><p>Check Plano logs for state storage initialization messages</p></li>
|
||
<li><p>Ensure the client is sending the <code class="docutils literal notranslate"><span class="pre">prev_response_id={$response_id}</span></code> from previous responses</p></li>
|
||
</ul>
|
||
</section>
|
||
<section id="best-practices">
|
||
<h2>Best Practices<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#best-practices" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#best-practices'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<ol class="arabic simple">
|
||
<li><p><strong>Use environment variables for credentials</strong>: Never hardcode database passwords in configuration files.</p></li>
|
||
<li><p><strong>Start with memory storage for development</strong>: Switch to PostgreSQL when moving to production.</p></li>
|
||
<li><p><strong>Implement cleanup policies</strong>: Prevent unbounded growth by regularly archiving or deleting old conversations.</p></li>
|
||
<li><p><strong>Monitor storage usage</strong>: Track conversation state table size and query performance in production.</p></li>
|
||
<li><p><strong>Test failover scenarios</strong>: Ensure your application handles storage backend failures gracefully.</p></li>
|
||
</ol>
|
||
</section>
|
||
<section id="next-steps">
|
||
<h2>Next Steps<a @click.prevent="window.navigator.clipboard.writeText($el.href); $el.setAttribute('data-tooltip', 'Copied!'); setTimeout(() => $el.setAttribute('data-tooltip', 'Copy link to this element'), 2000)" aria-label="Copy link to this element" class="headerlink" data-tooltip="Copy link to this element" href="#next-steps" x-intersect.margin.0%.0%.-70%.0%="activeSection = '#next-steps'"><svg height="1em" viewbox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"></path></svg></a></h2>
|
||
<ul class="simple">
|
||
<li><p>Learn more about building <a class="reference internal" href="../concepts/agents.html#agents"><span class="std std-ref">agents</span></a> that leverage conversational state</p></li>
|
||
<li><p>Explore <a class="reference internal" href="../concepts/filter_chain.html#filter-chain"><span class="std std-ref">filter chains</span></a> for enriching conversation context</p></li>
|
||
<li><p>See the <a class="reference internal" href="../concepts/llm_providers/llm_providers.html#llm-providers"><span class="std std-ref">LLM Providers</span></a> guide for configuring model routing</p></li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
</div><div class="flex justify-between items-center pt-6 mt-12 border-t border-border gap-4">
|
||
<div class="mr-auto">
|
||
<a class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors border border-input hover:bg-accent hover:text-accent-foreground py-2 px-4" href="prompt_guard.html">
|
||
<svg class="mr-2 h-4 w-4" fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||
<polyline points="15 18 9 12 15 6"></polyline>
|
||
</svg>
|
||
Guardrails
|
||
</a>
|
||
</div>
|
||
<div class="ml-auto">
|
||
<a class="inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors border border-input hover:bg-accent hover:text-accent-foreground py-2 px-4" href="../resources/tech_overview/tech_overview.html">
|
||
Tech Overview
|
||
<svg class="ml-2 h-4 w-4" fill="none" height="24" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewbox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
||
<polyline points="9 18 15 12 9 6"></polyline>
|
||
</svg>
|
||
</a>
|
||
</div>
|
||
</div></div><aside class="hidden text-sm xl:block" id="right-sidebar">
|
||
<div class="sticky top-16 -mt-10 max-h-[calc(100vh-5rem)] h-full overflow-y-auto pt-6 space-y-2"><p class="font-medium">On this page</p>
|
||
<ul>
|
||
<li><a :data-current="activeSection === '#how-it-works'" class="reference internal" href="#how-it-works">How It Works</a></li>
|
||
<li><a :data-current="activeSection === '#configuration-overview'" class="reference internal" href="#configuration-overview">Configuration Overview</a></li>
|
||
<li><a :data-current="activeSection === '#memory-storage-development'" class="reference internal" href="#memory-storage-development">Memory Storage (Development)</a></li>
|
||
<li><a :data-current="activeSection === '#postgresql-storage-production'" class="reference internal" href="#postgresql-storage-production">PostgreSQL Storage (Production)</a><ul>
|
||
<li><a :data-current="activeSection === '#prerequisites'" class="reference internal" href="#prerequisites">Prerequisites</a></li>
|
||
<li><a :data-current="activeSection === '#configuration'" class="reference internal" href="#configuration">Configuration</a></li>
|
||
<li><a :data-current="activeSection === '#supabase-connection-strings'" class="reference internal" href="#supabase-connection-strings">Supabase Connection Strings</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a :data-current="activeSection === '#troubleshooting'" class="reference internal" href="#troubleshooting">Troubleshooting</a></li>
|
||
<li><a :data-current="activeSection === '#best-practices'" class="reference internal" href="#best-practices">Best Practices</a></li>
|
||
<li><a :data-current="activeSection === '#next-steps'" class="reference internal" href="#next-steps">Next Steps</a></li>
|
||
</ul>
|
||
</div>
|
||
</aside>
|
||
</main>
|
||
</div>
|
||
</div><footer class="py-6 border-t border-border md:py-0">
|
||
<div class="container flex flex-col items-center justify-between gap-4 md:h-24 md:flex-row">
|
||
<div class="flex flex-col items-center gap-4 px-8 md:flex-row md:gap-2 md:px-0">
|
||
<p class="text-sm leading-loose text-center text-muted-foreground md:text-left">© 2025, Katanemo Labs, Inc Last updated: Feb 25, 2026. </p>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
</div>
|
||
<script src="../_static/documentation_options.js?v=f516ef65"></script>
|
||
<script src="../_static/doctools.js?v=9bcbadda"></script>
|
||
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
|
||
<script defer="defer" src="../_static/theme.js?v=582b20c5"></script>
|
||
<script src="../_static/design-tabs.js?v=f930bc37"></script>
|
||
</body>
|
||
</html> |