Skip to content

Repository files navigation

Product Feed Validator

Validate, score, and remediate merchant product feeds, returning clean exports plus merchant-facing readiness reports for AI/agent consumption.

Why this exists

As AI assistants become a product-discovery and shopping interface, catalog quality becomes part of machine-readable discoverability. This project explores that idea with a transparent pipeline: take a messy feed, normalize it into a stable product shape, explain what blocks agent use, and propose deterministic fixes.

The repository is a learning and experimentation project. It does not promise SEO ranking, product inclusion, or compatibility with any platform, and it is not an official validator from OpenAI, Shopify, or another vendor.

All committed examples are synthetic. Never commit merchant, affiliate-network, client, or account exports.

  • Language: Python 3.12
  • Framework: FastAPI + Uvicorn
  • Package management: uv
  • Linting/formatting: Ruff

Install

Prereqs: Python 3.12 and uv.

uv venv --python 3.12
source .venv/bin/activate
uv sync

Run

uv run uvicorn main:app --reload

Lint / Format

uv run ruff check .
uv run ruff format

Test

uv run pytest
uv run pytest tests/test_validator.py::test_name   # single test

API

POST /v1/feed/qa

Full pipeline: validate + sanitize. Returns cleaned feed + QA report.

POST /v1/feed/validate

Validate only; returns qa_report (no cleaning). Use for CI/CD or pre-merge checks.

POST /v1/feed/sanitize

Sanitize only; returns clean_openai_feed (no scoring). Use when you want cleaned output only.


All endpoints accept the same input formats:

Input formats (pick one):

Format How to send
JSON array or {"rows": [...]} Content-Type: application/json body
JSON Lines Content-Type: application/json body
CSV file Multipart form field file
Remote URL source_url query param or body field

Optional param: emit_jsonld=true — include schema.org JSON-LD per product. Optional param: profile=openai_chatgpt — choose the readiness profile. More profiles can be added without changing the API shape.

Example (JSON body):

curl -X POST http://localhost:8000/v1/feed/qa \
  -H "Content-Type: application/json" \
  -d '{"rows": [{"id": "SKU-1", "title": "Widget", "price": "9.99 USD", ...}]}'

Example (file upload):

curl -X POST http://localhost:8000/v1/feed/qa \
  -F "file=@feeds/good_example.json" \
  -F "emit_jsonld=false"

Response schema:

{
  "clean_openai_feed": {
    "json": [ /* normalized product rows */ ],
    "csv":  "/* CSV string */"
  },
  "clean_feed": {
    "json": [ /* same rows as clean_openai_feed */ ],
    "csv":  "/* same CSV string */"
  },
  "qa_report": {
    "agent_readiness_score": 0.0,
    "blocking_pass_rate": 0.0,
    "eligible_product_rate": 0.0,
    "profile": "openai_chatgpt",
    "rules_version": "2026.04.1",
    "subscores": {
      "identifiers":    0.0,
      "offer_integrity": 0.0,
      "content_quality": 0.0,
      "compliance": 0.0
    },
    "coverage": { /* per-field coverage percentages */ },
    "issue_summary": {
      "total_issues": 0,
      "blocking_count": 0
    },
    "issues": [
      {
        "row": 0,
        "field": "price",
        "type": "missing",
        "message": "...",
        "bucket": "blocking",
        "severity": "critical",
        "is_blocking": true
      }
    ],
    "generated_at": "2024-01-01T00:00:00Z"
  },
  "result_metadata": {
    "profile": "openai_chatgpt",
    "spec_path": "docs/openai_chatgpt_profile.md",
    "proposed_change_rows": 12
  },
  "proposed_changes": [
    {
      "row": 0,
      "id": "SKU-1",
      "change_count": 1,
      "changes": [
        {
          "field": "link",
          "op": "replace",
          "before": "example.com/p/1",
          "after": "https://example.com/p/1",
          "issue_types": ["normalized"],
          "reason": "use a full URL including the https:// scheme"
        }
      ]
    }
  ],
  "schema_org_jsonld": [ /* schema.org Product objects, if emit_jsonld=true */ ]
}

Scoring: 20% identifiers · 40% offer integrity · 30% content quality · 10% compliance (material, weight, return_policy, return_window). Includes blocking_pass_rate (rows passing all required fields).

Run Reports

Each persisted run also writes:

  • merchant_report.json — structured executive summary, grouped issues, top actions, coverage gaps
  • merchant_report.md — human-readable summary suitable for sharing with merchants or agencies

Retrieve them through:

  • GET /v1/runs/{run_id}/report
  • GET /v1/runs/{run_id}/report?format=md
  • pf export <run_id> --format report-json
  • pf export <run_id> --format report-md

See the project-defined openai_chatgpt readiness profile and the canonical platform references. This is an independent experimental validator, not an official platform certification tool.

For a concise snapshot of the current implementation, see docs/STATUS.md.

Security and deployment status

The current service is designed for local development and trusted inputs. It is not a hardened multi-tenant SaaS. In particular, production deployments should add authentication, tenant isolation, secret management, URL egress controls, request/response size limits, and an explicit data-retention policy.

Shopify tokens supplied to the API, CLI, or MCP tool are used for one sync and are not persisted. Feed runs do persist their raw and normalized rows locally under ignored runs/ and runs.db paths.

Please report security concerns privately rather than opening an issue that contains credentials or customer data.

Contributing and license

Contributions are welcome; see CONTRIBUTING.md. This project is released under the MIT License.

About

Validate, score, and remediate product feeds for AI commerce with FastAPI, CLI, MCP, and explainable readiness reports.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages