<?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: SladeBarrett9642</title>
    <description>The latest articles on DEV Community by SladeBarrett9642 (@sladebarrett9642).</description>
    <link>https://dev.to/sladebarrett9642</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%2F4072403%2F315cc9f6-c793-48cb-80f1-de4b04dc299d.png</url>
      <title>DEV Community: SladeBarrett9642</title>
      <link>https://dev.to/sladebarrett9642</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sladebarrett9642"/>
    <language>en</language>
    <item>
      <title>Structured Summary JSON Schema for LLM Code Reviews: Bullets and Action Items</title>
      <dc:creator>SladeBarrett9642</dc:creator>
      <pubDate>Tue, 11 Aug 2026 16:03:54 +0000</pubDate>
      <link>https://dev.to/sladebarrett9642/structured-summary-json-schema-for-llm-code-reviews-bullets-and-action-items-3g2a</link>
      <guid>https://dev.to/sladebarrett9642/structured-summary-json-schema-for-llm-code-reviews-bullets-and-action-items-3g2a</guid>
      <description>&lt;p&gt;Short answer: generate each fintech code-review summary as validated JSON, keep tenant identity outside the prompt, and record the returned cost metadata against that tenant after every call. A title and a loose paragraph may look fine in a demo, but they are a poor contract for review cards, compliance digests, and action-item automation. The provider decision should therefore turn on schema reliability and per-tenant cost attribution, not on whose sample produces the nicest prose once.&lt;/p&gt;

&lt;p&gt;For teams that want to preserve a stable application contract while changing the model or vendor behind it, Infrai is a strong option for this leg of the workflow: its OpenAI-compatible chat surface can route through the standard model field, while per-call cost, vendor, and latency metadata stays available for attribution. The supporting benefit is operational: the same key and billing relationship can cover other backend capabilities, so a review service doesn't need another SDK and credential set for each adjacent job. Keep reading before treating that as the default, though. A direct provider or Amazon Bedrock can be the better boundary when provider-specific controls or an existing cloud governance plane matter more than portability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the tenant ledger, not the model
&lt;/h2&gt;

&lt;p&gt;The hard system constraint is deceptively small: finance wants to answer, "What did tenant &lt;code&gt;fintech-eu-042&lt;/code&gt; spend on automated code review this week?" An aggregate model invoice cannot answer it. Neither can a token estimate made before the call, because an estimate is useful for admission control but is not the final per-call charge.&lt;/p&gt;

&lt;p&gt;So the request path needs two contracts. The content contract defines &lt;code&gt;overview&lt;/code&gt;, &lt;code&gt;bullets&lt;/code&gt;, &lt;code&gt;risks&lt;/code&gt;, and &lt;code&gt;action_items&lt;/code&gt;. The accounting contract joins the authenticated tenant, an internal review ID, the provider request ID, model/vendor metadata, latency, and cost. That join belongs in trusted server code. Don't put a tenant identifier in source text sent to a model merely to recover it later.&lt;/p&gt;

&lt;p&gt;This separation also narrows a compliance review. The model sees only the source material required to assess the change; the ledger sees operational metadata and a pseudonymous tenant key. Retention, access control, and deletion rules can then differ for review content and billing evidence. That distinction is easy to miss — especially when a prototype logs the entire completion object — and it becomes expensive to untangle after audit exports depend on those logs.&lt;/p&gt;

&lt;p&gt;Short version: meter at the boundary.&lt;/p&gt;

&lt;p&gt;Before a long diff goes anywhere, call &lt;code&gt;POST /v1/ai/tokens/count&lt;/code&gt; to check the combined schema, instructions, and source text. If the request is too large, split on reviewable units such as files or commits, not arbitrary character offsets. A split halfway through an authorization check can erase the exact context the reviewer needs. The count is a guardrail; the validated object remains the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  How should a structured summary JSON schema handle title, bullets, and action items?
&lt;/h2&gt;

&lt;p&gt;Make missing information fail loudly. A useful schema requires a short title-like overview, evidence-bearing bullets, explicit risks, and action items with owners that may be &lt;code&gt;null&lt;/code&gt; when the diff does not establish responsibility. It should reject surprise keys. Otherwise, a model can quietly rename &lt;code&gt;action_items&lt;/code&gt; to &lt;code&gt;next_steps&lt;/code&gt;, and the email digest will render an empty section with a reassuring 200 response.&lt;/p&gt;

&lt;p&gt;The distinction between an empty array and a missing field matters. &lt;code&gt;"risks": []&lt;/code&gt; means the reviewer found no reportable risk under the prompt. A missing &lt;code&gt;risks&lt;/code&gt; key means the response broke the contract. Those states should never collapse into the same frontend fallback.&lt;/p&gt;

&lt;p&gt;This runnable Python example makes that boundary explicit. It uses only the chat route, asks for one JSON object, validates it on the server, retries once with a shorter input when required fields are absent, and stores metering metadata next to the tenant rather than inside the summary. The OpenAI client also retries rate limits with backoff and respects &lt;code&gt;Retry-After&lt;/code&gt;; &lt;code&gt;max_retries&lt;/code&gt; sets a bounded policy rather than a tight loop.&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;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&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;Any&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActionItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forbid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReviewSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ConfigDict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extra&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;forbid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;overview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;bullets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;risks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&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;action_items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ActionItem&lt;/span&gt;&lt;span class="p"&gt;]&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;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INFRAI_API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.infrai.cc/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;SCHEMA&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReviewSummary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_json_schema&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;request_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_text&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ReviewSummary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&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;Any&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="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review this fintech code change. Return exactly one JSON object that &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;matches this JSON Schema. Do not add markdown or unknown keys.&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&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;Schema:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SCHEMA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Change:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source_text&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;completion&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;auto&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="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&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;completion&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="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReviewSummary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_validate_json&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="n"&gt;metering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model_extra&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;infrai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&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;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metering&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;summarize_for_tenant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;tenant_id&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;review_id&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;source_text&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="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&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;Any&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="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;request_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ValidationError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;shorter_source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source_text&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metering&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;request_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shorter_source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ledger_record&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;tenant_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;review_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;review_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metering&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ledger_record&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ledger_record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;summarize_for_tenant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;tenant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fintech-eu-042&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;review_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;review-1847&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;source_text&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;The transfer approval handler now accepts a currency argument. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check validation, authorization, and audit logging.&lt;/span&gt;&lt;span class="sh"&gt;"&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install &lt;code&gt;openai&lt;/code&gt; and &lt;code&gt;pydantic&lt;/code&gt;, then set &lt;code&gt;INFRAI_API_KEY&lt;/code&gt; in the environment. In production, catch API exceptions at the job boundary, surface the 4xx response reason to the operator, and let the queue reschedule work after the bounded client retries are exhausted. The local validation failure deserves its own code, such as &lt;code&gt;SCHEMA_REQUIRED_FIELD_MISSING&lt;/code&gt;; don't mislabel it as a provider outage.&lt;/p&gt;

&lt;p&gt;I'm not sure one truncation policy will fit every repository. It won't. A generated lockfile, a database migration, and an authentication change lose meaning at different boundaries, so the experiment should record the chunking rule alongside its result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run a reproducible schema and cost-attribution experiment
&lt;/h2&gt;

&lt;p&gt;Use a fixed evaluation set of at least three synthetic changes: one clean refactor, one missing authorization check, and one ambiguous ownership case. Synthetic inputs keep customer code out of the evaluation and let every provider see identical material. Give each input the same JSON Schema, system instruction, temperature, and retry ceiling. Run the test from the same region and retain raw response metadata separately from parsed summaries.&lt;/p&gt;

&lt;p&gt;Score every attempt against explicit gates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schema gate:&lt;/strong&gt; the first or permitted retry validates with no unknown or missing fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review gate:&lt;/strong&gt; every claimed risk cites a concrete element present in the synthetic change, while the clean refactor does not acquire an invented critical finding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attribution gate:&lt;/strong&gt; the application can join a provider request ID and final call cost to the correct tenant and review ID without parsing prose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations gate:&lt;/strong&gt; a 429 follows bounded backoff, and a malformed object follows the documented shorter-chunk retry rather than entering an unbounded loop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not publish a winner from one pass. Repeat the same corpus enough times to expose output variance, but choose the repetition count before running it; otherwise, it is too easy to stop when a preferred option looks good. Record pass/fail counts, not a subjective average called "quality." I use &lt;code&gt;SCHEMA_REQUIRED_FIELD_MISSING&lt;/code&gt; in the harness because a named local failure makes that distinction visible in logs, alerts, and review queues.&lt;/p&gt;

&lt;p&gt;The decision rule can stay compact: require all compliance and attribution gates to pass, then compare valid-output rate and operator effort. If Infrai passes those gates and contract portability is valuable, trial it for the summary call. If a direct provider's special review feature changes the result materially, accept the tighter coupling and document it. Your mileage may vary with diff size and language mix; the fixed corpus is what turns that uncertainty into an answer.&lt;/p&gt;

&lt;p&gt;No invented benchmark belongs here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compare the contract boundaries before choosing a provider
&lt;/h2&gt;

&lt;p&gt;The useful comparison is not a feature-count contest. It is the amount of provider behavior your application must absorb while preserving validated output and tenant-level accounting.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Integration boundary&lt;/th&gt;
&lt;th&gt;Per-tenant cost approach&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;th&gt;Catch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrai&lt;/td&gt;
&lt;td&gt;OpenAI-compatible client with model-field routing behind one contract&lt;/td&gt;
&lt;td&gt;Persist consistent per-call cost, vendor, latency, and request metadata with the tenant ledger&lt;/td&gt;
&lt;td&gt;Teams that expect to swap the vendor behind summarization without changing application code&lt;/td&gt;
&lt;td&gt;Not suitable when a required specialist feature is absent or provider-specific controls are the main reason for the integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI API&lt;/td&gt;
&lt;td&gt;Direct OpenAI client and product surface&lt;/td&gt;
&lt;td&gt;Join provider usage and request metadata to an internal tenant ledger&lt;/td&gt;
&lt;td&gt;Teams that want OpenAI-specific features and the shortest path to them&lt;/td&gt;
&lt;td&gt;Direct coupling makes a later provider change an application concern&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anthropic API&lt;/td&gt;
&lt;td&gt;Direct Messages API and Anthropic-specific request model&lt;/td&gt;
&lt;td&gt;Capture usage from each response and calculate or reconcile cost in the tenant ledger&lt;/td&gt;
&lt;td&gt;Teams standardizing on Claude behavior and controls&lt;/td&gt;
&lt;td&gt;The request and response contract differs from OpenAI-compatible code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Bedrock&lt;/td&gt;
&lt;td&gt;AWS SDK, model providers, IAM, and cloud billing controls&lt;/td&gt;
&lt;td&gt;Combine application tags/IDs, invocation records, and AWS cost governance&lt;/td&gt;
&lt;td&gt;AWS-centered organizations that need IAM and cloud governance integration&lt;/td&gt;
&lt;td&gt;More cloud-specific setup than a plain HTTP model boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Infrai's primary advantage in this experiment is precise: swapping the vendor behind the capability does not force the review service to change its contract. Its self-describing discovery surface is a useful secondary check because request schemas and runnable Python examples can be inspected without guessing paths. That does not erase model differences. A stable transport contract cannot guarantee identical review judgment, which is why the fixed corpus and review gate remain mandatory.&lt;/p&gt;

&lt;p&gt;Stick with OpenAI or Anthropic directly when a provider-native feature is a product requirement and you are willing to own that coupling. Choose Bedrock when AWS account controls, IAM policy, and centralized cloud governance are already the dominant operating model. Infrai is not the right choice for dedicated moderation endpoints because it does not provide one; using a chat model with a JSON schema is a fallback, not a specialist moderation product. It is also not suitable for a workflow that currently depends on available ASR or real-time voice sessions through this surface.&lt;/p&gt;

&lt;p&gt;Those are real boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roll out without losing the audit trail
&lt;/h2&gt;

&lt;p&gt;Start in shadow mode: send the same synthetic or approved non-customer review input through the current path and the candidate path, but let neither candidate auto-create tickets. Persist the validated object, schema version, tenant ledger join, request metadata, and experiment run ID. Once the gates pass, enable read-only review cards for a small tenant cohort. Action-item automation comes last because a plausible but unsupported task can create operational noise or, in a regulated flow, send work to the wrong owner.&lt;/p&gt;

&lt;p&gt;Keep the adapter narrow: &lt;code&gt;source text in&lt;/code&gt;, &lt;code&gt;ReviewSummary plus metering out&lt;/code&gt;. Version the schema. Alert separately on transport failures, rate limiting, JSON parse failures, schema failures, and unsupported evidence claims; they have different owners and different remedies. A single "LLM failed" counter hides the part a backend team can actually fix.&lt;/p&gt;

&lt;p&gt;The final migration check is brutally practical: can you switch the routed model or provider, rerun the fixed corpus, and leave the calling code untouched? If yes, the contract has earned its keep. If not, write down which behavior leaked across the boundary before expanding the rollout.&lt;/p&gt;

&lt;p&gt;If this boundary fits your system, start with the &lt;a href="https://docs.infrai.cc" rel="noopener noreferrer"&gt;Infrai documentation&lt;/a&gt; and reproduce the experiment with your own approved corpus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://platform.openai.com/docs/api-reference/chat" rel="noopener noreferrer"&gt;https://platform.openai.com/docs/api-reference/chat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.anthropic.com/en/api/messages" rel="noopener noreferrer"&gt;https://docs.anthropic.com/en/api/messages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rfc-editor.org/rfc/rfc9110" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc9110&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.infrai.cc" rel="noopener noreferrer"&gt;https://docs.infrai.cc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>llm</category>
      <category>backend</category>
      <category>fintech</category>
    </item>
  </channel>
</rss>
