File size: 6,993 Bytes
5733452
 
 
 
 
 
 
 
 
39707cc
 
5ff803a
 
 
39707cc
 
5ff803a
39707cc
5ff803a
 
 
 
39707cc
5ff803a
 
 
 
39707cc
5ff803a
 
39707cc
5ff803a
 
 
 
39707cc
5ff803a
39707cc
5ff803a
39707cc
5ff803a
 
 
 
39707cc
5ff803a
 
 
 
 
39707cc
5ff803a
 
39707cc
5ff803a
 
 
 
39707cc
5ff803a
 
39707cc
5ff803a
 
39707cc
5ff803a
 
 
 
39707cc
5ff803a
 
 
39707cc
5ff803a
 
39707cc
 
5ff803a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39707cc
 
5ff803a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39707cc
5ff803a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39707cc
5ff803a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
---
title: Dodge
emoji: "🚀"
colorFrom: blue
colorTo: indigo
sdk: docker
pinned: false
---

# Dodge Graph Query Assistant

Live Demo link: https://dodge-six-dusky.vercel.app/
Backend link: https://huggingface.co/spaces/parthnuwal7/dodge/tree/main?logs=container
Video link: https://youtu.be/Lt7ARqYgfI8

## What This Project Does
This project turns SAP Order-to-Cash data into a queryable graph and provides a chat-style interface to ask business questions in plain English.

In practice, it does three jobs well:
1. Build a graph model of the O2C process (Customer -> SalesOrder -> Delivery -> Invoice -> Payment).
2. Convert natural language into safe, schema-aware Cypher.
3. Return both tabular results and graph paths so answers are inspectable, not black-box.

Core stack:
- Backend: FastAPI, Neo4j, schema-guided LLM routing
- Frontend: React + Vite + Cytoscape
- Session persistence: Supabase (optional)

## Why This Design
Order-to-Cash is process data, not just records. Most high-value questions are multi-hop and relationship-heavy.

Examples:
- Which billed deliveries for customer X have no payment yet?
- Which plants are associated with delivered items in division 10?
- Which chains break between order and invoice?

These are awkward in relational query UIs but natural in a graph model. That is why Neo4j is central to the system, and why the query pipeline emphasizes path correctness.

## System Architecture

```mermaid
flowchart LR
	U[User Query] --> FE[Frontend: React + Chat Panel]
	FE --> API[/FastAPI: /api/v1/query/ask/]

	API --> GR[Guardrails]
	GR --> IE[Intent Extractor]
	IE --> TS[Template Selector]
	TS -->|known intent| TG[Template Cypher Generator]
	TS -->|complex intent| CG[Custom Cypher Generator]

	TG --> QV[Query Validator + Corrective Rewrites]
	CG --> QV

	QV --> NX[Neo4j Execution]
	NX --> PE[Path Extractor]
	PE --> RF[Response Formatter]
	RF --> FE

	FE --> SB[(Supabase Chat Logs)]
```

## Data Flow (Ingestion Side)
We treat ingestion as a schema-first process, not a blind import.

1. Load source entities from SAP O2C JSONL datasets.
2. Infer/curate graph schema (`nodes`, `edges`, ID fields, relationship joins).
3. Build normalized nodes and relationships in Neo4j.
4. Persist schema artifacts for runtime query validation and prompting.

Why this matters:
- The same schema powers ingestion, prompt constraints, and runtime query validation.
- This keeps generation and execution grounded to the actual graph shape.

## Query Flow (Runtime)
For `POST /api/v1/query/ask`, the backend pipeline is intentionally strict:

1. Input guardrail
2. Intent extraction
3. Domain/entity guardrail
4. Template route (deterministic) OR custom generation route
5. Cypher validation + corrective rewrite pass
6. Neo4j execution
7. Path extraction for graph highlighting
8. Response formatting (answer + explanation + cypher + rows + graph)

### Response Contract
Each answer returns:
- `answer` (human-readable)
- `explanation`
- `query_used` (final Cypher)
- `data` (tabular rows)
- `nodes`/`edges` (graph visualization payload)
- `metadata` (timing, intent, template, counts)

This is deliberate: users can verify what the system executed, not just trust the summary.

## Engineering Decisions and Tradeoffs

### 1) Neo4j as Source of Truth
We prioritized relationship traversal and path explainability over tabular convenience.

Benefits:
- Multi-hop traversal is concise and performant.
- Relationship direction is explicit (critical for process correctness).
- Native fit for path-based UI rendering.

Tradeoff:
- Requires strict schema/relationship governance to avoid drift.

### 2) Hybrid Query Strategy (Template-first)
We do not generate every query from scratch.

- Common intents use deterministic templates.
- Complex intents use custom LLM generation with schema and path constraints.

Why:
- Better reliability and lower variance for frequent asks.
- Flexibility when a query does not fit predefined intent classes.

### 3) FastAPI with Explicit Pipeline Boundaries
Services are split by responsibility (`query_router`, `query_validator`, `execution_engine`, `response_formatter`) so failures are stage-identifiable.

### 4) Explainability Over Minimal Payload
We intentionally return Cypher + path graph + table rows in one response to make debugging and trust easier in enterprise workflows.

## LLM Prompting Strategy
Prompting is schema-anchored and path-aware.

What we inject into prompts:
- Canonical node labels and properties
- Allowed relationship types and direction
- Candidate traversal skeletons from schema path search
- Identifier rules (primary keys, alternate IDs)
- Read-only constraints and output format requirements

This drastically reduces invalid relationship invention compared to unconstrained prompting.

## Guardrails and Validation
The system uses layered protection before query execution:

- Input guardrails: reject prompt-injection style patterns.
- Domain guardrails: check extracted entities against known graph schema.
- Output guardrails: enforce read-only query behavior.
- Schema validation: labels, relationships, directionality, malformed patterns.

Corrective rewrites are applied for common LLM mistakes (arrow syntax, invalid inline OR maps, EXISTS style fixes, path expressions in `WITH`).

When provider quotas are exhausted, the API surfaces a clear `429` (rate-limited) instead of opaque failures.

## Frontend Behavior
The UI has two modes:

1. Full Graph
2. Query Results (focused)

Query mode prioritizes relevant nodes/edges from returned paths and supports edge-focused highlighting from the side panel.

Chat behavior is session-based:
- Session ID is persisted client-side.
- History is restored from Supabase on reload.
- Quit clears session history and rotates to a new session.

## Deployment
Single repository, split deployment:

- Backend -> Hugging Face Space (Docker)
- Frontend -> Vercel (`frontend` root)

Frontend points to backend using `VITE_API_BASE_URL`.

## Repository Layout (Important Paths)
- `backend/app/api/` - HTTP routes
- `backend/app/services/` - orchestration services
- `backend/app/query/` - routing, validation, execution, formatting
- `backend/app/llm/` - intent extraction, prompt manager, template registry
- `backend/templates/cypher/` - deterministic Cypher templates
- `frontend/src/components/` - chat/graph panels
- `frontend/src/api/client.ts` - frontend API adapter

## Known Operational Constraints
- LLM providers can rate-limit (OpenRouter/Groq). Fallback is supported, but both can exhaust quota.
- Supabase chat logging requires service role key and existing `chat_logs` table.
- Neo4j schema quality directly affects custom-query reliability.

## Quick Start (Local)

Backend:
1. Set `backend/.env`
2. Run `uvicorn app.main:app --reload --port 8000`

Frontend:
1. Set `frontend/.env` if needed (`VITE_API_BASE_URL`)
2. Run `npm run dev` inside `frontend/`

Then open the frontend and start with any business question from the examples.