Run the API locally:
uv run fastapi dev src/mapi/main.py
The OpenAPI UI is available at:
http://127.0.0.1:8000/docs
Request:
curl -X POST http://127.0.0.1:8000/api/v1/row-progression/parse \
-H 'content-type: application/json' \
-d '{"code":"AA:DD,A:C,1:12,13=13W"}'
Response excerpt:
{
"code": "AA:DD,A:C,1:12,13=13W",
"rows": [
{"name": "AA", "position": 1},
{"name": "BB", "position": 2},
{"name": "CC", "position": 3},
{"name": "DD", "position": 4},
{"name": "13", "position": 20},
{"name": "13W", "position": 20}
]
}
A,B:C!,D
Result:
[
{"name": "A", "position": 1},
{"name": "D", "position": 4}
]
Rows B and C are gap markers. They consume positions 2 and 3 without being
returned as rows in this section.
Request:
curl -X POST http://127.0.0.1:8000/api/v1/row-progression/compress \
-H 'content-type: application/json' \
-d '{
"code": "manual",
"rows": [
{"name": "A", "position": 3},
{"name": "B", "position": 5},
{"name": "BW", "position": 5}
]
}'
Response:
{"code": "1:2!,A,4!,B=BW"}
The generated gap labels are canonical position placeholders. Parsing the compressed code returns the same row names and positions.
curl -X POST http://127.0.0.1:8000/api/v1/row-progression/build-venue \
-H 'content-type: application/json' \
-d '{
"venue_name": "Demo Arena",
"sections": [
{"name": "101", "code": "AA:DD,A:C,1:12,13=13W"},
{"name": "102", "code": "A,B:C!,D"}
]
}'
curl -X POST http://127.0.0.1:8000/api/v1/row-progression/venue-diff \
-H 'content-type: application/json' \
-d '{
"a": {"name": "Old Demo", "sections": {"101": "A:C"}},
"b": {"name": "New Demo", "sections": {"101": "A:B,D"}}
}'
Response:
{
"venue_diff": {
"101": {
"C": {"a": 3, "b": null},
"D": {"a": null, "b": 3}
}
}
}
curl -X POST http://127.0.0.1:8000/api/v1/row-progression/stats \
-H 'content-type: application/json' \
-d '{"code":"AA:CC,1=1W"}'
Response excerpt:
{
"code_type": "mixed",
"total_rows": 5,
"unique_row_count": 5,
"unique_position_count": 4,
"unique_names": ["1", "1W", "AA", "BB", "CC"],
"segment_count": 2
}
The Mapi agent is backed by Pydantic AI. By default it uses a local function model, so this works without an external API key.
curl -X POST http://127.0.0.1:8000/api/v1/row-progression/agent/analyze \
-H 'content-type: application/json' \
-d '{
"venue_id": "demo-arena",
"section_id": "101",
"code": "AA:CC,1=1W",
"question": "What should a broker review?"
}'
Response excerpt:
{
"analysis": {
"friendly_name": "Mapi is a friendly name for a mapping API...",
"parser_confidence": "valid",
"review_triggers": [
"Equivalent row alias detected; verify shared-position rows.",
"Range expansion used; validate start/end row family semantics."
],
"redis": {
"string_key": "venue:demo-arena:section:101:row_progression",
"hash_key": "venue:demo-arena:section:101",
"json_key": "venue:demo-arena:section:101:expanded",
"index_document_key": "venue:demo-arena:section:101:index"
}
},
"agent_model": "function:mapi-local"
}