<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>FastAPI on Vedant Andhale</title>
    <link>https://www.vedant.me/tags/fastapi/</link>
    <description>Recent content in FastAPI on Vedant Andhale</description>
    <image>
      <url>https://www.vedant.me/</url>
      <link>https://www.vedant.me/</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 10 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.vedant.me/tags/fastapi/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Crop Cure: a grape-leaf classifier inside WhatsApp</title>
      <link>https://www.vedant.me/projects/crop-cure/</link>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      
      <guid>https://www.vedant.me/projects/crop-cure/</guid>
      <description>A research prototype connecting a PyTorch image classifier to a multilingual WhatsApp workflow with FastAPI.</description>
      <content:encoded><![CDATA[<p>A model that accepts a tensor is useful to another developer. A model that accepts a photo in a familiar chat interface is easier for someone else to try.</p>
<p>Crop Cure connects a grape-leaf classifier to WhatsApp. A user chooses a language, sends a photograph, and receives a response based on the predicted class. The project supports English, Hindi and Marathi response flows.</p>
<h2 id="the-workflow">The workflow</h2>
<table>
	<thead>
			<tr>
					<th>Step</th>
					<th>Implementation</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Receive a message</td>
					<td>FastAPI and <code>pywa_async</code> handle the WhatsApp flow.</td>
			</tr>
			<tr>
					<td>Prepare the image</td>
					<td>Convert to RGB, resize, centre-crop and normalise.</td>
			</tr>
			<tr>
					<td>Run inference</td>
					<td>A custom PyTorch residual network with channel and spatial attention runs on the CPU.</td>
			</tr>
			<tr>
					<td>Decide whether to answer</td>
					<td>Compare the top softmax score with the configured threshold.</td>
			</tr>
			<tr>
					<td>Return the response</td>
					<td>Use the selected language and the predicted class.</td>
			</tr>
	</tbody>
</table>
<p>My work here spans the classifier integration and the surrounding application: getting an image from a message into the model, interpreting the output, and turning that output into a usable reply.</p>
<p>The model has four labels: black rot, esca, leaf blight and healthy. That is a deliberately narrow scope. It is not a general classifier for every crop or every possible leaf condition.</p>
<h2 id="a-threshold-needs-an-honest-description">A threshold needs an honest description</h2>
<p>The implementation uses a <code>0.98</code> confidence threshold. Predictions below it return <code>Unclassified</code>.</p>
<p>That number is <strong>not 98% accuracy</strong>. It is a threshold applied to a model output. A high softmax score can still be wrong, especially when the photograph differs from the data used to train the model. Lighting, background, camera distance and an unsupported plant all make the input harder to interpret.</p>
<p>The fallback gives the application a way to avoid returning a class for every image. Whether it rejects enough unsuitable images requires evaluation with representative photographs; the public repository does not establish field accuracy.</p>
<h2 id="where-the-prototype-stops">Where the prototype stops</h2>
<p>The repository describes the project as under development and not actively maintained. It is best understood as a research prototype, rather than a deployed agricultural diagnostic service.</p>
<p>Language preferences currently live in an in-memory dictionary, so they do not survive a process restart. Inference also runs synchronously inside the message-handling path. If I continued the project, I would persist conversation state, move inference into a controlled worker path, and evaluate class-specific errors and rejection behaviour on a held-out field dataset.</p>
<p>The interesting engineering lesson is how much of the product sits around the model. The preprocessing contract, supported inputs, fallback behaviour and message flow all determine whether the prediction can be used sensibly.</p>
<p>Implementation details: <a href="https://github.com/VedantAndhale/Crop_Cure_Bot/blob/main/src/model.py">model and preprocessing</a> and <a href="https://github.com/VedantAndhale/Crop_Cure_Bot/blob/main/main.py">WhatsApp application</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>FreightSense: making shipment recommendations inspectable</title>
      <link>https://www.vedant.me/projects/freightsense/</link>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      
      <guid>https://www.vedant.me/projects/freightsense/</guid>
      <description>A FastAPI decision-support prototype combining deterministic shipping calculations, LLM recommendations and a human override history.</description>
      <content:encoded><![CDATA[<p>A delayed shipment creates a practical question: should someone expedite it, offer a discount, or wait? A useful answer needs to account for the order&rsquo;s economics and explain the recommendation well enough for a person to challenge it.</p>
<p>FreightSense is my prototype for that workflow. It puts a rules-based calculation beside an LLM assessment, records both, and leaves the final decision with the operator.</p>
<h2 id="what-i-built">What I built</h2>
<p>The application combines a Python/FastAPI API, a small browser dashboard, a deterministic evaluation layer, Groq-hosted LLM calls and SQLite records. The repository also includes Docker and Cloud Run deployment configuration.</p>
<table>
	<thead>
			<tr>
					<th>Stage</th>
					<th>Responsibility</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Shipment input</td>
					<td>Collect the order, shipping and estimated delivery details.</td>
			</tr>
			<tr>
					<td>Deterministic evaluation</td>
					<td>Calculate delay, financial exposure and a weighted risk score; apply recommendation rules.</td>
			</tr>
			<tr>
					<td>LLM evaluation</td>
					<td>Interpret the same context and return a structured assessment.</td>
			</tr>
			<tr>
					<td>Comparison</td>
					<td>Show whether the two recommendations agree.</td>
			</tr>
			<tr>
					<td>Decision record</td>
					<td>Store the evaluation and any later human overrides.</td>
			</tr>
	</tbody>
</table>
<h2 id="keep-the-arithmetic-outside-the-prompt">Keep the arithmetic outside the prompt</h2>
<p>The deterministic layer calculates delay and exposure before the model sees the request. Its risk score combines delay severity, historical context, financial exposure and margin. The recommendation itself follows separate business rules: no delay can mean <code>NO_ACTION</code>; a feasible and economically acceptable intervention can mean <code>EXPEDITE</code>; otherwise the result can be <code>DISCOUNT</code> or <code>MONITOR</code>.</p>
<p>That distinction matters. A risk score describes the situation. It does not, by itself, prove that paying to expedite is sensible. The code checks conditions such as the shipping mode and the estimated intervention cost.</p>
<p>The LLM receives these calculations as context. The application parses its response, checks the recommendation label and bounds the confidence value. It also handles several failure paths, including API errors and invalid JSON, by returning the deterministic result with the LLM marked unavailable.</p>
<h2 id="disagreement-is-useful-information">Disagreement is useful information</h2>
<p>A model disagreeing with the rules should be visible. FreightSense stores the two assessments and flags the mismatch instead of quietly replacing one with the other.</p>
<p>The override endpoint lets a person record a different decision, a reason and outcome notes. Multiple overrides are retained as separate entries. That history makes it possible to revisit why an operator intervened, rather than seeing only the latest choice.</p>
<h2 id="what-the-prototype-establishes">What the prototype establishes</h2>
<p>The repository demonstrates the complete evaluation-and-review path. It does <strong>not</strong> establish a measured reduction in shipping costs. The financial figures are estimates, and the model&rsquo;s confidence is a self-reported value, not a calibrated probability of correctness.</p>
<p>For a production version, my next priorities would be durable shared storage, access control, stronger validation of model outputs, and a labelled evaluation set that includes expensive mistakes and ambiguous cases. Those are more useful next steps than adding another model to the comparison.</p>
<p>Implementation details: <a href="https://github.com/VedantAndhale/FreightSense/blob/main/app/core/deterministic.py">deterministic rules</a>, <a href="https://github.com/VedantAndhale/FreightSense/blob/main/app/core/llm_evaluator.py">LLM evaluation</a>, and <a href="https://github.com/VedantAndhale/FreightSense/blob/main/app/api/routes.py">API routes</a>.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
