fix: handle string attributes in graph ontology to prevent TypeError crash (fixes #135)

When the LLM returns ontology attributes as plain strings instead of
dicts, set_ontology() crashes with "TypeError: string indices must be
integers, not 'str'" at attr_def["name"].

Two-layer fix:
1. ontology_generator.py: normalize string attrs to {"name", "type",
   "description"} dicts during validation, so downstream code always
   receives well-formed structures.
2. graph_builder.py: add isinstance guard as a safety net in
   set_ontology() for both entity and edge attribute loops.

Co-Authored-By: Octopus <liyuan851277048@icloud.com>
This commit is contained in:
octo-patch 2026-04-26 11:29:26 +08:00 committed by 666ghj
parent 4064b68abf
commit a07841e91b
2 changed files with 24 additions and 4 deletions

View File

@ -233,8 +233,13 @@ class GraphBuilderService:
annotations = {} annotations = {}
for attr_def in entity_def.get("attributes", []): for attr_def in entity_def.get("attributes", []):
attr_name = safe_attr_name(attr_def["name"]) # 使用安全名称 # LLM may return attributes as strings instead of dicts
attr_desc = attr_def.get("description", attr_name) if isinstance(attr_def, str):
attr_name = safe_attr_name(attr_def)
attr_desc = attr_def
else:
attr_name = safe_attr_name(attr_def["name"]) # 使用安全名称
attr_desc = attr_def.get("description", attr_name)
# Zep API 需要 Field 的 description这是必需的 # Zep API 需要 Field 的 description这是必需的
attrs[attr_name] = Field(description=attr_desc, default=None) attrs[attr_name] = Field(description=attr_desc, default=None)
annotations[attr_name] = Optional[EntityText] # 类型注解 annotations[attr_name] = Optional[EntityText] # 类型注解
@ -257,8 +262,13 @@ class GraphBuilderService:
annotations = {} annotations = {}
for attr_def in edge_def.get("attributes", []): for attr_def in edge_def.get("attributes", []):
attr_name = safe_attr_name(attr_def["name"]) # 使用安全名称 # LLM may return attributes as strings instead of dicts
attr_desc = attr_def.get("description", attr_name) if isinstance(attr_def, str):
attr_name = safe_attr_name(attr_def)
attr_desc = attr_def
else:
attr_name = safe_attr_name(attr_def["name"]) # 使用安全名称
attr_desc = attr_def.get("description", attr_name)
# Zep API 需要 Field 的 description这是必需的 # Zep API 需要 Field 的 description这是必需的
attrs[attr_name] = Field(description=attr_desc, default=None) attrs[attr_name] = Field(description=attr_desc, default=None)
annotations[attr_name] = Optional[str] # 边属性用str类型 annotations[attr_name] = Optional[str] # 边属性用str类型

View File

@ -432,6 +432,11 @@ class OntologyGenerator:
entity_name_map[original_name] = entity["name"] entity_name_map[original_name] = entity["name"]
if "attributes" not in entity: if "attributes" not in entity:
entity["attributes"] = [] entity["attributes"] = []
# Normalize attributes: LLM may return strings instead of dicts
entity["attributes"] = [
attr if isinstance(attr, dict) else {"name": attr, "type": "text", "description": attr}
for attr in entity["attributes"]
]
if "examples" not in entity: if "examples" not in entity:
entity["examples"] = [] entity["examples"] = []
# 确保description不超过100字符 # 确保description不超过100字符
@ -456,6 +461,11 @@ class OntologyGenerator:
edge["source_targets"] = [] edge["source_targets"] = []
if "attributes" not in edge: if "attributes" not in edge:
edge["attributes"] = [] edge["attributes"] = []
# Normalize attributes: LLM may return strings instead of dicts
edge["attributes"] = [
attr if isinstance(attr, dict) else {"name": attr, "type": "text", "description": attr}
for attr in edge["attributes"]
]
if len(edge.get("description", "")) > 100: if len(edge.get("description", "")) > 100:
edge["description"] = edge["description"][:97] + "..." edge["description"] = edge["description"][:97] + "..."