<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Pratham Shettigar</title>
    <description>The latest articles on DEV Community by Pratham Shettigar (@pratham_shettigar_12).</description>
    <link>https://dev.to/pratham_shettigar_12</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4200399%2F31fc0185-83e7-4b90-a6b5-b8ebd835a77a.png</url>
      <title>DEV Community: Pratham Shettigar</title>
      <link>https://dev.to/pratham_shettigar_12</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pratham_shettigar_12"/>
    <language>en</language>
    <item>
      <title>Stop Hardcoding Prompts in Your Python Files: Meet PromptFill (best balance of curiosity and search terms)</title>
      <dc:creator>Pratham Shettigar</dc:creator>
      <pubDate>Thu, 22 Oct 2026 12:10:20 +0000</pubDate>
      <link>https://dev.to/pratham_shettigar_12/stop-hardcoding-prompts-in-your-python-files-meet-promptfill-best-balance-of-curiosity-and-search-30md</link>
      <guid>https://dev.to/pratham_shettigar_12/stop-hardcoding-prompts-in-your-python-files-meet-promptfill-best-balance-of-curiosity-and-search-30md</guid>
      <description>&lt;p&gt;Open any LLM-powered Python project that's been alive for a few weeks and you'll probably find the same thing: a function with a prompt inside it. Then the prompt grows a few more instructions, a formatting rule, an example. Before long, the logic is buried under a wall of text and nobody wants to touch the file.&lt;/p&gt;

&lt;p&gt;That's the itch behind &lt;strong&gt;PromptFill&lt;/strong&gt;, a small, zero-dependency Python package that lets you keep prompts in their own files and fill in the variables at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;promptfill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This post covers what it does, how it works under the hood (it's short enough to read in a minute), and where it fits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with prompts living inside code
&lt;/h2&gt;

&lt;p&gt;Prompts aren't really code, but we treat them like code because they happen to be strings. That causes predictable problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Readability suffers.&lt;/strong&gt; A long prompt in the middle of a function makes the actual logic hard to follow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse means copy-paste.&lt;/strong&gt; Need the same prompt in another script? You copy it, and now you have two versions to keep in sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing values hide until runtime.&lt;/strong&gt; Forget one variable and you may not notice until a half-formed prompt reaches a model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of a form letter. Nobody retypes the whole letter for each recipient. You keep one template with blanks like &lt;em&gt;Dear ___&lt;/em&gt;, and fill in the names when it's time to send. Prompts deserve the same treatment: one template file, blanks filled in by your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What PromptFill does
&lt;/h2&gt;

&lt;p&gt;PromptFill loads a prompt template from a text file, replaces the placeholders with values you pass from Python, and returns a plain string. That's the whole job, and it's deliberately small.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write your prompt in a &lt;code&gt;.md&lt;/code&gt; or &lt;code&gt;.txt&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Add placeholders wherever you need dynamic values.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;load_prompt()&lt;/code&gt; and supply those values.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External prompt files&lt;/strong&gt; in Markdown or plain text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic formatting&lt;/strong&gt; with values supplied from Python&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable templates&lt;/strong&gt; across scripts and projects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing variable detection&lt;/strong&gt; with a clear &lt;code&gt;ValueError&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A clear &lt;code&gt;FileNotFoundError&lt;/code&gt;&lt;/strong&gt; when the path is wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No third-party dependencies&lt;/strong&gt;, only the standard library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider-agnostic output&lt;/strong&gt;: the result is a string you can hand to Gemini, OpenAI, or anything else&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A quick walkthrough
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Write the template
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;reviewer.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert $role.

Review the following code written in $language for bugs,
security vulnerabilities, and performance issues.

Code to review:

$code_snippet

Provide your findings and suggest improvements.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Placeholders use a &lt;code&gt;$&lt;/code&gt; prefix: &lt;code&gt;$role&lt;/code&gt;, &lt;code&gt;$language&lt;/code&gt;, &lt;code&gt;$code_snippet&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Load and fill it
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;promptfill&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_prompt&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewer.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Senior Security Engineer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code_snippet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;print(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Get the result
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are an expert Senior Security Engineer.

Review the following code written in Python for bugs,
security vulnerabilities, and performance issues.

Code to review:

print('Hello, World!')

Provide your findings and suggest improvements.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The return value is a regular Python string, so it drops straight into whatever client you already use.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;The whole implementation fits on one screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Prompt file not found at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substitute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Missing prompt variable for placeholder: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what happens when you call it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It checks the file exists.&lt;/strong&gt; If the path doesn't point to a file, you get a &lt;code&gt;FileNotFoundError&lt;/code&gt; that includes the path, so a typo is easy to spot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It reads the file as UTF-8 text.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It builds a &lt;code&gt;string.Template&lt;/code&gt; from the contents.&lt;/strong&gt; This is the standard library's built-in &lt;code&gt;$placeholder&lt;/code&gt; substitution, which is why placeholders look the way they do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It calls &lt;code&gt;substitute()&lt;/code&gt; with your keyword arguments.&lt;/strong&gt; Every &lt;code&gt;$name&lt;/code&gt; in the template is replaced with the matching value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If a placeholder has no matching value, it raises a &lt;code&gt;ValueError&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;Template&lt;/code&gt; raises a &lt;code&gt;KeyError&lt;/code&gt; internally, and PromptFill catches it and re-raises it as a &lt;code&gt;ValueError&lt;/code&gt; with a message that names the missing variable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I chose &lt;code&gt;string.Template&lt;/code&gt; over f-strings or &lt;code&gt;str.format()&lt;/code&gt; on purpose. It only cares about &lt;code&gt;$&lt;/code&gt;, so curly braces in your prompt (a JSON example, say) aren't treated as placeholders. And since it's part of the standard library, there's nothing extra to install, audit, or keep updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusing one template for many jobs
&lt;/h2&gt;

&lt;p&gt;The real payoff comes when one prompt serves several cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;promptfill&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_prompt&lt;/span&gt;

&lt;span class="n"&gt;template_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewer.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;python_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python Developer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code_snippet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;print(10 + 20)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;javascript_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JavaScript Developer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;JavaScript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code_snippet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;console.log(10 + 20)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No duplicated text and no drift between copies. Improve the wording in &lt;code&gt;reviewer.md&lt;/code&gt; and every caller benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catching missing variables early
&lt;/h2&gt;

&lt;p&gt;Say your template expects &lt;code&gt;$role&lt;/code&gt;, &lt;code&gt;$language&lt;/code&gt;, and &lt;code&gt;$code_snippet&lt;/code&gt;, but you forget &lt;code&gt;language&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;promptfill&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_prompt&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewer.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Developer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code_snippet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;x = 1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueError: Missing prompt variable for placeholder: 'language'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fails loudly, and it names the exact variable you forgot. That beats sending an incomplete prompt to a model and wondering why the output is strange.&lt;/p&gt;

&lt;h2&gt;
  
  
  Good to know
&lt;/h2&gt;

&lt;p&gt;Because PromptFill builds on &lt;code&gt;string.Template&lt;/code&gt;, a few of its standard behaviors carry over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Literal dollar signs need escaping.&lt;/strong&gt; To include a real &lt;code&gt;$&lt;/code&gt; in a prompt (for example, a price), write &lt;code&gt;$$&lt;/code&gt; in the template file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;${name}&lt;/code&gt; also works.&lt;/strong&gt; Use it when a placeholder sits right next to other text, like &lt;code&gt;${language}_review&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extra keyword arguments are ignored.&lt;/strong&gt; Passing a value the template doesn't use won't raise an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Plugging it into an LLM call
&lt;/h2&gt;

&lt;p&gt;PromptFill only formats prompts. It doesn't call any model itself, so you're free to use whichever provider you like. Here's an example with an OpenAI-compatible client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;promptfill&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_prompt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reviewer.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python Expert&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;code_snippet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;print(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YOUR_MODEL_NAME&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The OpenAI SDK is installed separately (&lt;code&gt;pip install openai&lt;/code&gt;) and only for this example. PromptFill itself stays dependency-free.&lt;/p&gt;

&lt;h2&gt;
  
  
  A project layout that scales
&lt;/h2&gt;

&lt;p&gt;Once prompts are files, organizing them is easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-ai-project/
├── prompts/
│   ├── reviewer.md
│   ├── summarizer.md
│   └── chatbot.txt
├── main.py
├── requirements.txt
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file per prompt, named for its purpose. It also makes prompt changes easy to review: a diff on &lt;code&gt;prompts/summarizer.md&lt;/code&gt; reads like a diff on a document, not a tangle of string literals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who it's for
&lt;/h2&gt;

&lt;p&gt;PromptFill is aimed at anyone who keeps prompts in Python projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI engineers&lt;/strong&gt; building LLM-powered apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RAG developers&lt;/strong&gt; maintaining retrieval and Q&amp;amp;A prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent developers&lt;/strong&gt; managing a different prompt per agent or task&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python developers&lt;/strong&gt; who just want cleaner code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A note on security
&lt;/h2&gt;

&lt;p&gt;PromptFill is a text formatter. It doesn't make prompts or inserted values safe on its own, so a few habits still apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't put API keys or passwords in prompt files.&lt;/li&gt;
&lt;li&gt;Treat user-provided values as untrusted input.&lt;/li&gt;
&lt;li&gt;Review formatted prompts before using them in sensitive applications.&lt;/li&gt;
&lt;li&gt;Remember that sending a prompt to an external provider sends its contents to that provider.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;PromptFill is young and the roadmap is open. Ideas I'm considering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better error messages for invalid templates&lt;/li&gt;
&lt;li&gt;Support for nested prompt directories&lt;/li&gt;
&lt;li&gt;Template validation&lt;/li&gt;
&lt;li&gt;Prompt version management&lt;/li&gt;
&lt;li&gt;Integration examples for LangChain and other frameworks&lt;/li&gt;
&lt;li&gt;Unit tests and CI&lt;/li&gt;
&lt;li&gt;Support for additional template formats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If one of these matters to you, tell me. Feedback shapes what gets built first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;promptfill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/shettigarpratham6-web/promptfill" rel="noopener noreferrer"&gt;shettigarpratham6-web/promptfill&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyPI:&lt;/strong&gt; &lt;a href="https://pypi.org/project/promptfill/" rel="noopener noreferrer"&gt;pypi.org/project/promptfill&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I'd love to hear how it goes. Open an issue, suggest a feature, or send a pull request. And if it saves you from one more 80-line string in a Python file, a star on the repo goes a long way.&lt;/p&gt;

&lt;p&gt;How do you manage prompts in your projects: inline strings, files, or something else? Let me know in the comments.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
