Skip to content

feat(examples): add HS Code Classifier trade compliance agent#569

Open
Subhajitdas99 wants to merge 5 commits into
GetBindu:mainfrom
Subhajitdas99:examples/hs-code-classifier
Open

feat(examples): add HS Code Classifier trade compliance agent#569
Subhajitdas99 wants to merge 5 commits into
GetBindu:mainfrom
Subhajitdas99:examples/hs-code-classifier

Conversation

@Subhajitdas99
Copy link
Copy Markdown
Contributor

@Subhajitdas99 Subhajitdas99 commented May 30, 2026

Summary

  • Problem: No trade compliance agents exist in Bindu examples, despite the roadmap explicitly targeting global trade compliance as the core business vertical.
  • Why it matters: HS code classification is the first step in every international shipment. Get it wrong and you face wrong tariffs, blocked goods, or fines. SMBs currently have no affordable tool for this.
  • What changed: Added examples/hs-code-classifier/ — an Agno + OpenRouter agent that classifies any product into its 6-digit HS code, provides indicative duty rates for China/India → EU/US trade routes, flags compliance risks, and lists alternative codes.
  • What did NOT change: No changes to core Bindu framework, server, extensions, or any existing examples.

Change Type

  • Feature

Scope

  • Documentation

Linked Issue/PR

Related: Bindu trade compliance roadmap (Discord, 29 May 2026)

User-Visible / Behavior Changes

New example agent at examples/hs-code-classifier/. Requires OPENROUTER_API_KEY. No changes to existing behaviour.

Security Impact

  • New permissions/capabilities? No
  • Secrets/credentials handling changed? No
  • New/changed network calls? Yes — agent calls OpenRouter API at runtime when handling a request
  • Database schema/migration changes? No
  • Authentication/authorization changes? No
  • Risk + mitigation: API key loaded from environment variable only, never hardcoded. # pragma: allowlist secret added to suppress false-positive detection.

Verification

Environment

  • OS: Windows 10
  • Python version: 3.12
  • Storage backend: In-memory
  • Scheduler backend: In-memory

Steps to Test

  1. cd examples/hs-code-classifier
  2. cp .env.example .env and set OPENROUTER_API_KEY
  3. python hs_code_classifier.py
  4. Send: Classify cotton t-shirts for adults
  5. Reply plain to the clarifying question

Expected Behavior

  • Agent starts and registers DID successfully
  • HS Classification Skill v1.0.0 loads
  • Returns HS CODE / CLASSIFICATION RATIONALE / DUTY RATES / COMPLIANCE NOTES / ALTERNATIVE CODES

Actual Behavior

Matches expected. Correctly returned 6109.10 with 12% EU duty, compliance notes, and alternative codes. DID signature present on every response.

Evidence

  • Test output / logs

Human Verification

  • Verified scenarios: Agent starts, skill loads, DID registered, full classification returned end-to-end via A2A JSON-RPC
  • Edge cases checked: Empty query → clear error message returned. Ambiguous product description → agent asks one clarifying question before classifying
  • What you did NOT verify: Classification accuracy across all HS chapters — recommend spot-checking against the official WCO tariff database before production use

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Database migration needed? No

Failure Recovery

  • Remove examples/hs-code-classifier/ to revert — self-contained, no framework changes
  • No known bad symptoms

Risks and Mitigations

  • Risk: HS code suggestions may be incorrect for edge cases or unusual product types
    • Mitigation: README and agent output both include a disclaimer to verify against official tariff schedules before filing a customs declaration

Checklist

  • Pre-commit hooks pass
  • Documentation updated
  • Security impact assessed
  • Human verification completed
  • Backward compatibility considered

Summary by CodeRabbit

  • New Features

    • Added an HS code classifier example app that returns HS codes with rationale, duty estimates, risk flags, trade-agreement detection, alternatives, and clarifying questions.
  • Documentation

    • Added a comprehensive README with setup steps, example queries, expected output format, and project structure.
    • Included declarative skill examples and worked workflows for the classifier.
  • Configuration

    • Added an environment template entry for the required API key.

Agno + OpenRouter agent that classifies products into 6-digit HS codes,
provides duty rates for China/India → EU/US trade routes, flags compliance
risks, and lists alternative codes. Part of Bindu's trade compliance toolchain.
Verified locally end-to-end with cotton t-shirts classification query.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 30, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2a9f6bf7-8a55-4f2a-9309-82e2588bd21e

📥 Commits

Reviewing files that changed from the base of the PR and between 5db52b7 and 13cf7b3.

📒 Files selected for processing (1)
  • examples/hs-code-classifier/hs_code_classifier.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • examples/hs-code-classifier/hs_code_classifier.py

📝 Walkthrough

Walkthrough

Adds an HS code classifier example: README and env template, a Bindu HTTP handler that validates input and calls an OpenRouter-backed Agent using a strict Markdown prompt, startup wiring, and a declarative HS classification skill YAML.

Changes

HS Code Classifier Example

Layer / File(s) Summary
Documentation and Environment Setup
examples/hs-code-classifier/README.md, examples/hs-code-classifier/.env.example, examples/hs-code-classifier/hs_code_classifier.py (docstring)
README gives usage, features, setup, env vars, project layout, compliance notes; .env.example adds OPENROUTER_API_KEY placeholder; module docstring documents endpoint and example queries.
Settings, Instructions, and Agent Initialization
examples/hs-code-classifier/hs_code_classifier.py (AppSettings, INSTRUCTIONS, agent, config)
Adds AppSettings (pydantic-settings) with startup validation, defines strict Markdown INSTRUCTIONS, initializes global Agent with OpenRouter, and defines Bindu config.
HTTP Handler and Service Startup
examples/hs-code-classifier/hs_code_classifier.py (handler, __main__ entrypoint)
handler(messages) selects latest user message, rejects empty product descriptions, calls agent.run(input=messages), logs exceptions with stack trace, and entrypoint runs validate_settings() then bindufy(config, handler).
Skill Declaration and Examples
examples/hs-code-classifier/skills/hs-classification-skill/skill.yaml
YAML skill with metadata, features, tags, two worked examples, capability descriptions, explicit output structure, and A2A JSON-RPC over HTTP transport settings.

Sequence Diagram

sequenceDiagram
  participant Client
  participant BinduHTTP as Bindu HTTP
  participant Handler as hs_code_classifier.handler
  participant Agent as Agent(OpenRouter)
  Client->>BinduHTTP: POST user messages
  BinduHTTP->>Handler: invoke handler(messages)
  Handler->>Agent: agent.run(input=messages)
  Agent->>Handler: classification Markdown result
  Handler->>BinduHTTP: return HTTP response
  BinduHTTP->>Client: HTTP response with result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • Paraschamoli
  • raahulrahl

Poem

🐰 I hopped through docs and code with cheer,
I taught HS codes both far and near.
I asked the web for duties and law,
Wrote clear marks so humans saw.
Now packages and tariffs meet my rhyme,
I nibble carrots, one at a time.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly and accurately summarizes the main change: adding a new HS Code Classifier trade compliance agent example.
Description check ✅ Passed The PR description comprehensively covers all required template sections: summary with problem/why/what changed, change type, scope, security impact with mitigation, verification with environment/steps/behavior, evidence, human verification, compatibility, and failure recovery.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Subhajitdas99
Copy link
Copy Markdown
Contributor Author

Hey @raahulrahl 👋 Just opened PR #569 — HS Code Classifier, the first trade compliance agent for Bindu.

It classifies any product into its 6-digit HS code, provides duty rates for China/India → EU/US, flags compliance risks, and lists alternative codes. Directly aligned with the trade compliance roadmap you shared.

• Verified end-to-end (correctly classified cotton t-shirts as 6109.10 with 12% EU duty)
• Multi-turn conversation — asks one clarifying question when product description is ambiguous
• DID signature on every response
• Zero changes to core framework

#569

Happy to keep building out the compliance toolchain — CBAM calculator or EUDR checker next if useful.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
examples/hs-code-classifier/hs_code_classifier.py (1)

163-165: ⚡ Quick win

Use the repo logger for startup messages.

These print() calls should be get_logger(__name__) calls to stay consistent with the repo’s Python logging convention.

As per coding guidelines, "Use get_logger(__name__) from bindu/utils/logging.py for logging, not print()".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/hs-code-classifier/hs_code_classifier.py` around lines 163 - 165,
Replace the two print() startup messages in the module-level __main__ block with
the repository logger: import get_logger from bindu.utils.logging (if missing),
create logger = get_logger(__name__), and call logger.info(...) for the existing
messages (e.g. logger.info("🛃 HS Code Classifier running at
http://localhost:3773") and logger.info("📦 Example: Classify cotton t-shirts
for adults")) so logging follows the repo convention instead of using print().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/hs-code-classifier/.env.example`:
- Line 1: Add the secret-scan allowlist pragma comment to the .env.example
template so pre-commit secret scanners won't flag the example API key; insert
the line "# pragma: allowlist secret" at the top of the .env.example file (above
the OPENROUTER_API_KEY=your_openrouter_api_key_here entry) and keep the example
placeholder value unchanged.

In `@examples/hs-code-classifier/hs_code_classifier.py`:
- Around line 54-78: The prompt in hs_code_classifier.py currently asks for DUTY
RATES, COMPLIANCE NOTES, and ALTERNATIVE CODES without any authoritative data
source; either remove/disable those sections and restrict output to HS
classification only (keep the RULES and ask one clarifying question if
ambiguous), or implement and document a dated external data lookup (e.g., call
to a tariff API or citation variable) and include the source/date alongside any
duty/trade-agreement statements; update the prompt text blocks named "DUTY
RATES", "COMPLIANCE NOTES", and "ALTERNATIVE CODES" accordingly so the code
producing the prompt (the prompt constant or function that generates it) no
longer asserts tariff/regulatory facts without a referenced source.
- Around line 83-85: The code reads OPENROUTER_API_KEY and BINDU_DEPLOYMENT_URL
directly from os.environ when constructing the OpenRouter model (see the
model=OpenRouter call), which violates the config guideline; replace those
os.getenv calls with values from app_settings (use
app_settings.OPENROUTER_API_KEY and app_settings.BINDU_DEPLOYMENT_URL) and add a
startup-time validation step (e.g., in module init or a dedicated
validate_settings function invoked at import/startup) that raises a clear error
if the key/URL are missing or empty so the service fails fast instead of at
first request; apply the same replacement/validation for the other occurrences
noted around lines 113-115.
- Around line 155-156: In hs_code_classifier.py replace the current except
Exception as e block that returns f"Classification error: {str(e)}" with logic
that logs the full exception using get_logger(__name__) from
bindu/utils/logging.py (capture and log the exception/traceback) and instead
returns a generic, non-sensitive message like "Classification failed" to
callers; locate the except block (the one catching Exception as e) to implement
the change and ensure you do not expose str(e) in the returned value.

---

Nitpick comments:
In `@examples/hs-code-classifier/hs_code_classifier.py`:
- Around line 163-165: Replace the two print() startup messages in the
module-level __main__ block with the repository logger: import get_logger from
bindu.utils.logging (if missing), create logger = get_logger(__name__), and call
logger.info(...) for the existing messages (e.g. logger.info("🛃 HS Code
Classifier running at http://localhost:3773") and logger.info("📦 Example:
Classify cotton t-shirts for adults")) so logging follows the repo convention
instead of using print().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c503576e-0205-419a-8fe0-b1c10def180d

📥 Commits

Reviewing files that changed from the base of the PR and between 682a19c and b2a5851.

📒 Files selected for processing (4)
  • examples/hs-code-classifier/.env.example
  • examples/hs-code-classifier/README.md
  • examples/hs-code-classifier/hs_code_classifier.py
  • examples/hs-code-classifier/skills/hs-classification-skill/skill.yaml

Comment thread examples/hs-code-classifier/.env.example
Comment thread examples/hs-code-classifier/hs_code_classifier.py
Comment thread examples/hs-code-classifier/hs_code_classifier.py Outdated
Comment thread examples/hs-code-classifier/hs_code_classifier.py Outdated
- Add AppSettings validation for required environment variables
- Use pydantic_settings for config management instead of os.getenv()
- Replace print() with get_logger() for consistent logging
- Log full exception with traceback, return generic error message
- Add secret-scan allowlist pragma to .env.example
- Update README with pydantic-settings dependency
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/hs-code-classifier/hs_code_classifier.py`:
- Around line 45-55: AppSettings currently calls os.getenv inside field defaults
which prevents pydantic's BaseSettings from loading env vars; remove the
os.getenv calls and declare the fields so BaseSettings can populate them (e.g.,
make openrouter_api_key a plain str field and set bindu_deployment_url with the
literal default "http://localhost:3773" or use pydantic Field(...,
env="BINDU_DEPLOYMENT_URL")/Field(..., env="OPENROUTER_API_KEY") if you want
explicit env names), leaving the inner Config with env_file intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9bb41029-1b42-485b-a1e9-fac8889e8a65

📥 Commits

Reviewing files that changed from the base of the PR and between b2a5851 and 0918c25.

📒 Files selected for processing (3)
  • examples/hs-code-classifier/.env.example
  • examples/hs-code-classifier/README.md
  • examples/hs-code-classifier/hs_code_classifier.py
✅ Files skipped from review due to trivial changes (2)
  • examples/hs-code-classifier/.env.example
  • examples/hs-code-classifier/README.md

Comment thread examples/hs-code-classifier/hs_code_classifier.py
Copy link
Copy Markdown
Member

@Paraschamoli Paraschamoli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @Subhajitdas99 i think we already have good number for example agent

Addresses CodeRabbit major issue: agent now searches authoritative sources
before emitting tariff figures. Duty rate tables include source citations
and official TARIC/HTS verification links.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
examples/hs-code-classifier/hs_code_classifier.py (1)

216-217: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix Agno Agent.run(...) usage in hs_code_classifier handler (return type + call signature)

  • In agno 2.5.2, Agent.run() returns a RunOutput object (not a plain string). Returning it directly from the Bindu handler may not produce a usable text response—return result.content (or the appropriate text field) instead.
  • Agent.run() does not natively accept input= as a way to pass a list of chat-message dicts. agent.run(input=messages) is likely incorrect—update the call to use the supported prompt/history mechanism, and only pass messages if that kwarg is valid.
  • Double-check that providing the full messages list doesn’t duplicate Bindu’s own prepended system prompt when using instructions=INSTRUCTIONS.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/hs-code-classifier/hs_code_classifier.py` around lines 216 - 217,
Agent.run currently is being called incorrectly and its return is a RunOutput
object; replace the call to avoid using the unsupported input= keyword and
return the textual field from the RunOutput. Specifically, call agent.run with
the messages list as a positional prompt/history argument (e.g.,
agent.run(messages) rather than agent.run(input=messages)), then return
result.content (or the appropriate text attribute from RunOutput) instead of the
whole result object; also ensure the messages you pass do not include a
duplicated system prompt when instructions=INSTRUCTIONS is used (omit the
prepended system message or only pass user/assistant turns).
🧹 Nitpick comments (1)
examples/hs-code-classifier/hs_code_classifier.py (1)

60-61: 💤 Low value

Optional: prefer model_config = SettingsConfigDict(...) over the legacy class Config in AppSettings
pydantic-settings v2 deprecates the inner class Config in favor of model_config, e.g. for env_file.

♻️ Suggested change
-from pydantic_settings import BaseSettings
+from pydantic_settings import BaseSettings, SettingsConfigDict
@@
-    class Config:
-        env_file = ".env"
+    model_config = SettingsConfigDict(env_file=".env")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/hs-code-classifier/hs_code_classifier.py` around lines 60 - 61,
Replace the legacy inner class Config used for env file configuration in
AppSettings with the pydantic v2 pattern: define a module-level or class
attribute model_config on AppSettings using SettingsConfigDict to set env_file
(e.g. model_config = SettingsConfigDict({"env_file": ".env"})); update the
AppSettings class to remove the inner class Config and import SettingsConfigDict
from pydantic_settings to apply the same env_file behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/hs-code-classifier/hs_code_classifier.py`:
- Line 234: Add a trailing newline at end-of-file to satisfy the
end-of-file-fixer pre-commit hook: open the file containing the bindufy(config,
handler) call and ensure there is a single newline character after the final
line (after the bindufy(config, handler) statement), then save and re-run the
pre-commit checks or commit the change.

---

Outside diff comments:
In `@examples/hs-code-classifier/hs_code_classifier.py`:
- Around line 216-217: Agent.run currently is being called incorrectly and its
return is a RunOutput object; replace the call to avoid using the unsupported
input= keyword and return the textual field from the RunOutput. Specifically,
call agent.run with the messages list as a positional prompt/history argument
(e.g., agent.run(messages) rather than agent.run(input=messages)), then return
result.content (or the appropriate text attribute from RunOutput) instead of the
whole result object; also ensure the messages you pass do not include a
duplicated system prompt when instructions=INSTRUCTIONS is used (omit the
prepended system message or only pass user/assistant turns).

---

Nitpick comments:
In `@examples/hs-code-classifier/hs_code_classifier.py`:
- Around line 60-61: Replace the legacy inner class Config used for env file
configuration in AppSettings with the pydantic v2 pattern: define a module-level
or class attribute model_config on AppSettings using SettingsConfigDict to set
env_file (e.g. model_config = SettingsConfigDict({"env_file": ".env"})); update
the AppSettings class to remove the inner class Config and import
SettingsConfigDict from pydantic_settings to apply the same env_file behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 477af21f-515b-4fd2-a509-0050bd22f3e1

📥 Commits

Reviewing files that changed from the base of the PR and between 5672967 and 5db52b7.

📒 Files selected for processing (1)
  • examples/hs-code-classifier/hs_code_classifier.py

Comment thread examples/hs-code-classifier/hs_code_classifier.py Outdated
@Subhajitdas99
Copy link
Copy Markdown
Contributor Author

Hey @Paraschamoli — understood, happy to defer to the team's judgment on example count.

One thing worth noting: this one is directly aligned with Raahul's trade compliance roadmap — HS code classification is the first step in every international shipment, and it demonstrates Bindu's A2A + DID capabilities in a real commercial use case rather than a general-purpose demo.

Also just pushed a fix addressing CodeRabbit's major concern — added DuckDuckGoTools so duty rates are sourced from live web search rather than LLM memory, with TARIC/HTS source citations in every response.

Happy to close if the team prefers to keep examples focused elsewhere — just wanted to flag the trade compliance angle. Up to you and @raahulrahl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants