Skip to content

Commit 921e067

Browse files
namedgraphclaude
andcommitted
Recognise top-level JSON-LD in Operation.process_json
A dict carrying any JSON-LD reserved key (@context, @graph, @id, @type) is now parsed to an rdflib.Graph by Operation.process_json itself, ahead of the existing generic-dict recursion. Previously every consuming op (POST, PUT, Merge, ldh-Create*/Add*) re-parsed the same JSON-LD payload in its own execute_json — the runtime treated bare JSON-LD as opaque JSON to recurse into, only the ops at the boundary knew its semantics. Symmetric with the bare-value auto-wrap (json_to_rdflib): a JSON scalar becomes a Literal, a JSON-LD object becomes a Graph. Unblocks the chat-extraction case: an LLM that emits JSON-LD as its final answer (image metadata, structured extraction) gets a real Graph result the renderer can lay out, rather than a dict of Literals that renders as raw text. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7e79866 commit 921e067

1 file changed

Lines changed: 26 additions & 1 deletion

File tree

src/web_algebra/operation.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from abc import ABC, abstractmethod
2+
import json
23
import logging
34
from typing import Type, Dict, Optional, Any, List, ClassVar, Union
45
from pydantic import BaseModel, Field, ConfigDict
@@ -9,6 +10,13 @@
910
from rdflib.query import Result
1011

1112

13+
# JSON-LD keyword set used to recognise a top-level dict as RDF data rather
14+
# than generic JSON to recurse into. Presence of any of these at the root is
15+
# a strong, unambiguous signal — they are JSON-LD reserved terms with no
16+
# legitimate meaning in non-RDF JSON.
17+
_JSONLD_KEYS = ("@context", "@graph", "@id", "@type")
18+
19+
1220
class Operation(ABC, BaseModel):
1321
"""
1422
Abstract base class for all operations with dual execution paths:
@@ -91,7 +99,24 @@ def process_json(
9199
# Return RDFLib objects as-is for operation chaining
92100
return result
93101

94-
# 🔁 Recurse into each value — allows nested @op inside JSON-LD and SPARQL bindings
102+
# JSON-LD shape recognition — a dict carrying any JSON-LD reserved
103+
# key is RDF data, not generic JSON to recurse into. Parse it
104+
# once at the runtime layer so every consuming op (POST, PUT,
105+
# Merge, ldh-Create*/Add*) receives an `rdflib.Graph` directly
106+
# instead of re-parsing identically inside its own
107+
# `execute_json`. Symmetric with the bare-value auto-wrap
108+
# below: that branch turns a JSON scalar into the matching
109+
# RDFLib term; this branch turns a JSON-LD object into the
110+
# matching RDFLib graph.
111+
if any(k in json_data for k in _JSONLD_KEYS):
112+
graph = Graph()
113+
graph.parse(data=json.dumps(json_data), format="json-ld")
114+
return graph
115+
116+
# 🔁 Recurse into each value — allows nested @op inside generic
117+
# JSON structures (e.g. SPARQL binding objects), without
118+
# collapsing pure-data dicts that JSON-LD detection above
119+
# already handled.
95120
return {
96121
k: cls.process_json(settings, v, context, variable_stack)
97122
for k, v in json_data.items()

0 commit comments

Comments
 (0)