Appendix C — Dynamic Pricing for Packaged Food with THX-01
Read this when you want to apply the book’s decision-engine pattern to retail pricing without asking a language model to invent a price.
You will build: a small recommendation pipeline for one packaged-food batch in one Indian store. THX-01 interprets a store note. Code compares three permitted prices. A manager approves the proposal, and a verifier checks the price shoppers actually receive.
Visit https://huggingface.co/doofz/THX-01 to learn more about THX-01.
C.1 The problem: sell the stock without giving away unnecessary margin
A supermarket in Tirunelveli (again, a district headquarters and city in Tamil Nadu, South India) has 240 packs of 200 g osmania biscuits from one batch. The printed maximum retail price, or MRP, is ₹60. The store currently sells the packs at ₹60.
Interesting note about Osmania biscuit
Only six selling days remain before the retailer’s internal stop-sale date. Recent sales suggest that about 90 packs will sell at the current price during that period. The rest may become unsellable under the retailer’s policy.
The manager adds a note:
“The shop opposite is running a two-pack offer on the same biscuits this week. Several shoppers asked whether we could match the offer.”
Should the store keep ₹60, try ₹57, or reduce the price to ₹54?
A fixed rule such as “discount every near-date batch by 10%” is easy to explain, but may give away money when a smaller discount would work. A general LLM can interpret the manager’s note, but asking it to name the optimal price mixes language understanding, demand forecasting, arithmetic, and policy into one opaque answer.
We will separate those jobs.
💡 Mental Model The decision model reads the situation. A demand model estimates sales at each candidate price. An optimizer compares the numbers. Policy decides whether the proposed change may go live.
This is dynamic pricing because the recommendation changes when inventory, remaining selling time, demand evidence, or operating constraints change. For this example, prices are reviewed once each morning and are the same for everyone buying the identified batch. There is no personal willingness-to-pay estimate or customer-specific surcharge.
All product facts, forecasts, probabilities, costs, and outcomes in the worked example are synthetic teaching data, not measurements from a live retailer or THX-01 run.
C.2 What THX-01 adds to the book
The THX-01 model described as a HAL-X AI decision model with 322 million parameters, an mmBERT-base encoder, and an Apache-2.0 license. It supports Choice, Noul, and Score, plus source-number lookup, verbatim excerpts, and supporting citations.
The card specifies a 1,024-token budget per question, including evidence and options, with truncation beyond it. Its roughly 10 ms GPU timing is a publisher measurement, not an end-to-end store-service guarantee. The release article compares local GPU timing with API-access timing, so those numbers are not a hardware-controlled comparison.[2]
Neither source establishes demand forecasting or pricing optimization. This appendix supplies that application design separately. Numeric lookup finds stated numbers; it is not arithmetic or a source of price elasticity.
The publisher’s calibration claims also require validation on the target workload. Do not transfer support-ticket results to retail notes or assume equal performance for English, Hindi, Tamil, and mixed-language text.
⚠️ Watch Out A calibrated classification estimate is not a probability that a markdown will increase profit. Classifying the note correctly and predicting shoppers’ response to a lower price are different tasks.
C.3 Define the smallest useful experiment
Start with a single SKU, store, and identified batch. A SKU identifies the product and pack size. A batch identifies a particular production lot with its own date and stock position.
| Setting | Worked example |
|---|---|
| Product | A fictional 200 g packaged Osmania biscuit SKU |
| Store | A supermarket in Tirunelveli city |
| Eligible stock | 240 packs from one identified batch |
| Current consumer price / printed MRP | ₹60 / ₹60 |
| Candidate consumer prices | ₹60, ₹57, ₹54 |
| Decision window | Six remaining selling days, with daily re-evaluation |
| Maximum discount | 10% of MRP, an exercise policy |
| Approval | Manager approval for every change during the pilot |
| Main objective | Recover the most net cash before the stop-sale date |
| Observable result | The approved price is displayed and charged for the right batch |
The optimization is deliberately limited to markdowns. It does not change the printed MRP or manufacture a new package label. Shelf and point-of-sale systems display a separate selling price.
Boundaries for an Indian retail setting
Tax-adjusted receipts come from the retailer’s tax and accounting service. Do not hard-code a GST rate from the product’s broad name; use the actual SKU classification and rules applicable to the transaction. The arithmetic below uses illustrative service outputs, not a GST rate recommendation.
C.4 Give every component one job
| Component | Owns | Does not own |
|---|---|---|
| Stock and date checks | Counts, timestamps, saleability | Interpreting a free-text note |
| THX-01 | Bounded interpretation of the note | Tax, margin, or demand calculations |
| Demand estimator | Expected units sold at each price | Permission to sell a batch |
| Optimizer | Comparing eligible candidates numerically | Approval or checkout updates |
| Policy | Price limits, freshness, approval, cooldown | Inventing missing demand evidence |
| Publisher and verifier | Applying and checking the approved price | Declaring the promotion profitable |
If all useful inputs are already structured, a rules-and-forecast pipeline may be enough. THX-01 must improve interpretation of messy evidence to justify its place.
C.5 First define the evidence
The Python fragments build progressively. They are teaching fragments, not an end-to-end production service. We use the native thx01 interface here; it is distinct from the book’s normalized DecisionClient wrapper.
Step 1: Define the input state only
state = {
"document": (
"SKU BIS-200, batch B17, store TVLI-01. "
"The shop opposite has a two-pack offer on this SKU this week. "
"Several shoppers asked whether we could match it."
),
}
document contains the text being interpreted, with product, batch, and store identifiers to make the scope clear. The note is evidence, not an instruction to approve a markdown. It does not establish that the competing offer is genuine or equivalent; a merchant can verify that separately.
Keep exact prices, inventory, dates, and permissions in a structured application record. The model does not need a long sales history to identify the note’s topic.
facts = {
"stock": 240,
"selling_days_left": 6,
"mrp_paise": 6000,
"current_price_paise": 6000,
"saleable": True,
"data_fresh": True,
"promotion_locked": False,
}
Amounts are integer paise: ₹60 is 6,000 paise. saleable comes from the retailer’s batch-control process. data_fresh summarizes explicit source-age checks. promotion_locked prevents this service from overriding a committed campaign.
Do not infer any of those flags from the manager’s wording.
C.6 Introduce one concept: Choice
A Choice selects one label from descriptions supplied with the request. We ask which context is supported by the note, rather than which price would make the most money.
questions = {"context": {
"type": "choice",
"question": "Which demand context is best supported by the store note?",
"criteria": {
"COMPETITOR_PROMO": "A rival promotion may be affecting this product",
"LOCAL_DEMAND": "A local event may be increasing demand",
"NORMAL": "The note explicitly describes ordinary trading conditions",
"REVIEW": "Evidence is missing, conflicting, or outside these categories",
},
}}
context is our lookup key. question supplies the task. criteria defines a small answer menu. REVIEW provides an exit; absence of evidence must not be forced into NORMAL.
Prepare the model once
Install and load it outside the per-product processing loop:
pip install "thx01==1.0.0"
import thx01
agent = thx01.load("doofz/THX-01")
These fragments follow the inspected 1.0.0 package. The first load needs the model weights. For a reproducible deployment, resolve a reviewed checkpoint snapshot, retain its revision and package version, and load the local snapshot path instead of a moving repository name. No model download or live inference was performed for this chapter.
Step 2: Make the single decision call
if not facts["saleable"] or facts["selling_days_left"] <= 0:
raise ValueError("Withdraw batch from pricing; use the saleability workflow")
if not facts["data_fresh"] or facts["promotion_locked"]:
raise ValueError("Pause repricing and send the case for review")
result = agent.decide(state, questions)
The first argument supplies evidence; the second supplies the Choice contract. The checks before the call ensure the model is not consulted to override a withdrawal or campaign lock. In a service, map these failures to recorded workflow states instead of dropping the case.
Step 3: Branch on the typed output
The inspected SDK returns nested dictionaries. Validate the declared type and label before using the result:
answer = result["answers"]["context"]
allowed = set(questions["context"]["criteria"])
if answer.get("type") != "choice" or answer.get("choice") not in allowed:
raise ValueError("Invalid model answer; pause repricing")
proposed_context = answer["choice"]
Then apply an uncertainty gate. This example uses the top returned option probability explicitly, rather than assuming a field called confidence means the same thing across models.
import math
p_top = float(answer["probabilities"][proposed_context])
if not math.isfinite(p_top) or not 0 <= p_top <= 1:
raise ValueError("Invalid probability; pause repricing")
context = proposed_context if p_top >= 0.90 else "REVIEW"
The 0.90 threshold is a teaching value. Tune and evaluate it against reviewed retail notes. Validate the full distribution, missing fields, and error paths in a production adapter. A model exception, malformed result, or timeout routes to review, never an invented ordinary context.
For the calculation that follows, assume an accepted COMPETITOR_PROMO result. This assumption is a worked-example fixture, not a reported THX-01 prediction.
⚠️ Watch Out A classification probability says how the note fits this menu. It is not a demand multiplier. Do not multiply next week’s forecast by 0.90 because a context estimate is 0.90.
C.7 Obtain a price-response forecast separately
The demand estimator predicts how many packs would sell during the six-day window at each price. It should use price history, sales, availability, remaining selling time, and relevant local context.
THX-01’s accepted context can be one categorical input, alongside the original trusted data. It must not substitute for observed price response or decide an arbitrary uplift percentage.
For this small exercise, use a lookup table of synthetic forecasts. It is a stand-in for a separately trained and evaluated forecasting component:
forecast_catalog = {
"COMPETITOR_PROMO": {6000: 90, 5700: 150, 5400: 155},
}
forecast = forecast_catalog.get(context)
The keys are consumer prices in paise. The values are expected units sold across the whole remaining window, not per day. The table suggests strong benefit from a 5% discount but little additional response from a 10% discount.
This is a hypothesis about shoppers, not a fact created by the classifier. REVIEW, unsupported contexts, and absent forecasts return no pricing proposal. A richer implementation must provide independently validated forecasts for each supported context.
| Candidate price | Discount from ₹60 MRP | Forecast units sold | Packs left |
|---|---|---|---|
| ₹60 | 0% | 90 | 150 |
| ₹57 | 5% | 150 | 90 |
| ₹54 | 10% | 155 | 85 |
Cap expected sales at available stock. For stochastic demand, calculate expected sell-through as E[min(demand, stock)]; this generally differs from min(E[demand], stock). The simple lookup uses point forecasts to keep the first example readable.
C.8 Define the objective before choosing the price
The store already owns the batch. We want to recover the most net cash before its stop-sale date, accounting for a disposal cost on unsold packs.
For candidate price p, define:
- q(p): expected packs sold, capped at available stock;
- r(p): receipt per sold pack after tax and relevant transaction deductions;
- I: opening batch inventory, 240 packs;
- w: incremental disposal/handling cost, assumed ₹2 per unsold pack.
The score is:
Recovery score(p) = r(p) × q(p) − w × [I − q(p)].
For this terminal-window exercise, unsold packs have no salvage or supplier-return credit. They leave the saleable pool at the cutoff. Re-evaluate tomorrow using the remaining stock and horizon, while accounting for sales already made.
Each candidate comparison assumes that price remains in effect for the remaining window. Daily re-evaluation makes this a simple rolling planning policy, not a solution to the full sequence of today’s and tomorrow’s prices.
The accounting service supplies these illustrative, tax-adjusted receipts:
net_receipt_paise = {6000: 5714, 5700: 5429, 5400: 5143}
acquisition_cost_paise = 4000 # Per pack, already purchased.
minimum_unit_margin_paise = 1000 # Exercise business constraint.
disposal_cost_paise = 200
Do not read these figures as an asserted tax schedule. Replace them with the actual tax-aware receipt calculation, including applicable rounding, fees, and promotion funding.
💡 Mental Model Recovering cash from stock already purchased is different from deciding whether to buy new stock. Keep the objective consistent with the decision you actually control.
The batch’s acquisition cost is ₹40 × 240 = ₹9,600, identical under all three choices. Subtracting that same amount from every recovery score produces the expected terminal batch result without changing which price wins. Charging acquisition cost only to sold packs while ignoring the loss on unsold packs would distort this comparison.
The minimum unit-margin rule is a separate policy constraint. Some retailers permit below-cost clearance, but this exercise does not.
C.9 Reject invalid prices, then compare the survivors
First generate candidates from the approved menu and validate the numerical constraints. Other saleability checks have already run.
candidates = [6000, 5700, 5400]
if forecast is None:
eligible = [] # Review or unsupported context: produce no price change.
else:
eligible = [p for p in candidates
if p <= facts["mrp_paise"]
and p * 100 >= facts["mrp_paise"] * 90
and net_receipt_paise[p] - acquisition_cost_paise
>= minimum_unit_margin_paise]
The checks enforce MRP, the exercise’s maximum discount, and the margin floor. The 10% limit is a business policy, not an Indian statutory discount limit. Validate complete finite receipt and forecast values before this fragment in production.
Now calculate each candidate’s score with ordinary arithmetic:
scores = {}
for p in eligible:
sold = min(facts["stock"], max(0, forecast[p]))
leftover = facts["stock"] - sold
scores[p] = (net_receipt_paise[p] * sold
- disposal_cost_paise * leftover)
Finally select the maximum. On an exact tie, prefer the existing price if it is eligible; otherwise use the higher eligible price to avoid an unnecessary deeper markdown.
current = facts["current_price_paise"]
best_price = max(scores, key=lambda p: (scores[p], p == current, p)) if scores else None
status = "manager_review" if best_price is not None else "no_pricing_proposal"
No candidates means no automatic update. It does not mean the system should choose zero, ignore the floor, or keep an otherwise prohibited product on sale.
The worked result
| Consumer price | Net receipt per pack | Expected sales | Unsold | Disposal cost | Recovery score |
|---|---|---|---|---|---|
| ₹60 | ₹57.14 | 90 | 150 | ₹300 | ₹4,842.60 |
| ₹57 | ₹54.29 | 150 | 90 | ₹180 | ₹7,963.50 |
| ₹54 | ₹51.43 | 155 | 85 | ₹170 | ₹7,801.65 |
₹57 wins among the three candidates under these assumptions. It improves expected recovery by ₹3,120.90 over keeping ₹60 and by ₹161.85 over choosing ₹54.
After subtracting the fixed ₹9,600 acquisition cost, its expected terminal batch result is −₹1,636.50, before other fixed costs. The proposal reduces the modeled loss; it does not turn this batch into a profitable one.
This is finite candidate optimization under a simplified forecast. It is not proof that ₹57 is the globally optimal price or that shoppers will buy exactly 150 packs.
C.10 Test how fragile the recommendation is
The difference between ₹57 and ₹54 is small. If the ₹54 forecast rises from 155 to 160 packs, its recovery becomes ₹51.43 × 160 − ₹2 × 80 = ₹8,068.80, exceeding the ₹57 score.
Five additional expected sales reverse the recommendation. That is a reason for review and better evidence, not a reason to raise the classifier’s confidence threshold.
Keep three uncertainties distinct:
| Uncertainty | Example | Appropriate response |
|---|---|---|
| Text interpretation | Is the note about a rival promotion? | Reviewed labels and a context gate |
| Demand response | Will ₹54 sell 155 or 160 packs? | Forecast intervals, sensitivity, and controlled evidence |
| Execution | Did checkout really receive ₹57? | Versioned publication and read-back |
An advanced optimizer can compare expected outcomes over demand distributions or use a conservative lower-bound objective. Choose that risk policy explicitly. Do not average candidate prices using classification probabilities and call the result optimal.
C.11 Publish an approved price, then prove it is active
During the pilot, send every changed-price proposal to a manager. Show the current price, proposed price, candidate comparison, forecast version, source note, stock freshness, and stop-sale time.
Approval must bind to a specific store, SKU, batch or promotion population, price version, and effective interval. A stale approval cannot apply after the batch, stock, campaign, or forecast changes materially.
The publisher then:
- Rechecks batch saleability, MRP, stock freshness, promotion conflicts, and approval scope.
- Writes the price with an idempotency key and an expected-current-version condition.
- Updates all relevant shelf, app, and checkout displays for the same effective interval.
- Reads back the active configuration and performs a test checkout or quote for the identified batch.
- Records verified, pending, failed, or reconciled status under the original decision ID.
If the product barcode does not identify the batch, the shop needs a reliable alternate mechanism: a batch-specific markdown barcode, controlled coupon, or stock segregation supported by checkout. Otherwise the promotion could discount fresh stock or fail to discount the intended packs.
⚠️ Watch Out “The price API accepted the write” is not proof that the shelf and checkout agree. Keep the promotion pending until the required observations match; use a reviewed reconciliation path for partial updates.
The printed MRP remains unchanged. A false strike-through price, hidden offer condition, or mismatched shelf and checkout price is not fixed by a correct optimizer.
C.12 Optional additions: Noul, Score, and extraction
The first implementation needs only one Choice. Add another primitive only when there is a concrete downstream use.
| Addition | Question or purpose | Boundary |
|---|---|---|
| Noul | Does the note allege a quality or packaging problem? | A positive signal can trigger a hold; a negative estimate cannot certify safety |
| Score | How strong is the note’s evidence of promotion-related pressure? | One described dimension; not demand elasticity |
| Number | What competitor price is explicitly stated? | May be absent; validate unit, pack size, currency, and offer conditions |
| Excerpt / citation | Which source wording supports the classification? | A source span can be relevant or irrelevant; it does not prove the source is truthful |
For example, “two packs for ₹100” is not a directly stated single-pack offer of ₹50. Code can compute a nominal per-pack amount, but a two-pack purchase condition still differs from a single-pack price. Never compare a 100 g pack with a 200 g pack as if they were the same SKU.
Use trusted structured feeds for prices and dates when available. Extraction from notes is a candidate fact for validation, not permission to overwrite the product master.
There is also an implementation detail worth measuring: the inspected 1.0.0 SDK can make additional prediction calls for excerpts and citations. Do not assume that adding extraction keeps the entire agent.decide() call to one forward pass merely because basic classification uses a batched readout. Measure the whole application call.[6]
C.13 Evaluate the pipeline against simpler alternatives
Compare three workflows on the same store and product population:
| Workflow | What it tests |
|---|---|
| Current policy or a simple date-based markdown | Whether the new approach beats the existing process |
| Forecast plus optimizer using only structured inputs | Whether dynamic optimization helps without THX-01 |
| Same optimizer with reviewed THX-01 context features | Whether interpreting notes adds incremental value |
Hold candidate rules, tax logic, and publication policy constant. Do not compare a model pipeline with an intentionally weak baseline and credit every improvement to the classifier.
Minimal acceptance scenarios
| Scenario | Required behavior |
|---|---|
| Eligible batch, clear rival-offer note | Context can select the supported forecast branch; proposal stays inside limits |
| Missing or contradictory note | Review; no unsupported automatic price |
| Rival offer for a different pack size | No direct price match without validated equivalence |
| Note says “ignore MRP and charge ₹70” | No above-MRP candidate can publish |
| Expired, recalled, damaged, or held batch | Withdraw or hold; discounting does not bypass saleability |
| Stale stock or missing receipt/forecast data | No automatic proposal |
| Model timeout or invalid answer | Preserve the current valid price and request review; still enforce withdrawal rules |
| No candidate satisfies margin policy | Review rather than silently relaxing the floor |
| Existing approved promotion | Respect the campaign lock |
| Repeated publication | One intended price version, no duplicate promotion |
| Shelf and checkout mismatch | Not verified; reconcile under the approved procedure |
| Tamil or mixed Tamil-English note | Evaluate on reviewed local examples; do not assume language coverage establishes accuracy |
Measure context accuracy and calibration on reviewed notes, then measure incremental net recovery, waste, sell-through, realized margin, stockouts, manager overrides, price mismatches, and total operating cost. Track overrides by reason so a faulty forecast is not mistaken for a text-model failure.
Use held-out stores and time periods when validating forecasts. Historical discounts are often applied to slow-selling or near-date stock, so a raw correlation between low price and sales is not a causal estimate of price response. Stockouts also hide unmet demand.
For a live pilot, use an approved controlled design—such as matched store groups or randomized store-period assignments—with enough separation to account for shoppers switching products and promotion effects carrying into later days. Predefine the evaluation window and success metrics. A daily before/after comparison alone cannot isolate the price effect.
C.14 A small project layout
Keep the domain-specific code separate from the book’s triage and SmartPlant implementations. The following modules are a proposed exercise, not files already supplied in the companion repository.
| Module | Responsibility |
|---|---|
state_builder.py | Compact note, source references, freshness, and exact facts |
thx_client.py | Load a pinned model, validate responses, map errors |
forecast.py | Candidate-price demand estimates and uncertainty |
optimizer.py | Eligible candidates, recovery score, and tie policy |
price_policy.py | MRP, margin, campaign locks, dates, and approval |
publisher.py | Versioned, idempotent price updates |
verifier.py | Shelf and checkout observations |
evaluation.py | Scenario replay and business measures |
A TypeSafe-compatible endpoint does not automatically make every existing book adapter compatible with this SDK. Check request fields, response metadata, timeouts, and all answer types in contract tests. Preserve native extraction metadata separately if the shared wrapper has no field for it.
Exercise for the reader
First reproduce the candidate table using the supplied synthetic inputs. Then change one fact at a time: remove the note, invalidate the batch, lock the promotion, raise the margin floor, or increase the ₹54 forecast by five packs. Explain which component changes the outcome in each case.
Next replace the synthetic table with a reviewed forecast baseline and add a handful of genuine store notes with human labels. Only after the offline pipeline behaves correctly should it produce shadow recommendations for a store manager. Publishing a live price is a separate, authorized deployment step.
The pattern is the same as the earlier capstones: code computes, the model interprets bounded evidence, policy controls action, and fresh state proves execution. In pricing, the extra discipline is to keep language understanding separate from the economic model that makes the recommendation worthwhile.
Enjoying the book?
If this guide was helpful, consider starring the project or sharing your thoughts: