hedit 0.7.6.dev2__tar.gz → 0.7.6.dev3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hedit-0.7.6.dev2/hedit.egg-info → hedit-0.7.6.dev3}/PKG-INFO +1 -1
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3/hedit.egg-info}/PKG-INFO +1 -1
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/pyproject.toml +1 -1
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/annotation_agent.py +34 -10
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/hed_comprehensive_guide.py +19 -9
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/version.py +1 -1
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_annotation_agent.py +126 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_comprehensive_guide.py +13 -27
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/LICENSE +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/PKG_README.md +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/README.md +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/hedit.egg-info/SOURCES.txt +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/hedit.egg-info/dependency_links.txt +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/hedit.egg-info/entry_points.txt +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/hedit.egg-info/requires.txt +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/hedit.egg-info/top_level.txt +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/setup.cfg +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/assessment_agent.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/evaluation_agent.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/feedback_summarizer.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/feedback_triage_agent.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/state.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/validation_agent.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/vision_agent.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/agents/workflow.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/api/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/api/main.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/api/models.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/api/security.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/api_executor.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/client.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/commands/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/config.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/executor.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/local_executor.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/main.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/cli/output.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/scripts/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/scripts/process_feedback.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/telemetry/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/telemetry/collector.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/telemetry/schema.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/telemetry/storage.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/error_remediation.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/github_client.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/hed_rules.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/image_processing.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/json_schema_loader.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/litellm_llm.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/openrouter_llm.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/utils/schema_loader.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/validation/__init__.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/validation/hed_lsp.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/src/validation/hed_validator.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_api_endpoints.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_cli_client.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_cli_config.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_cli_integration.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_cli_main.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_error_remediation.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_feedback_integration.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_feedback_triage.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_github_client.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_hed_lsp.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_integration_openrouter.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_json_schema_loader.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_keyword_extraction.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_litellm_llm.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_no_extend_propagation.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_openrouter_llm.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_schema_loader.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_security.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_state.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_telemetry.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_validation.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_validation_agent.py +0 -0
- {hedit-0.7.6.dev2 → hedit-0.7.6.dev3}/tests/test_version.py +0 -0
|
@@ -12,7 +12,7 @@ from langchain_core.messages import HumanMessage, SystemMessage
|
|
|
12
12
|
|
|
13
13
|
from src.agents.state import HedAnnotationState
|
|
14
14
|
from src.utils import extract_text_content
|
|
15
|
-
from src.utils.hed_comprehensive_guide import get_comprehensive_hed_guide
|
|
15
|
+
from src.utils.hed_comprehensive_guide import format_semantic_hints, get_comprehensive_hed_guide
|
|
16
16
|
from src.utils.json_schema_loader import HedJsonSchemaLoader, load_latest_schema
|
|
17
17
|
|
|
18
18
|
logger = logging.getLogger(__name__)
|
|
@@ -60,21 +60,23 @@ class AnnotationAgent:
|
|
|
60
60
|
self,
|
|
61
61
|
vocabulary: list[str],
|
|
62
62
|
extendable_tags: list[str],
|
|
63
|
-
semantic_hints: list[dict] | None = None,
|
|
64
63
|
no_extend: bool = False,
|
|
65
64
|
) -> str:
|
|
66
65
|
"""Build the system prompt for the annotation agent.
|
|
67
66
|
|
|
67
|
+
The system prompt is static per schema version (vocabulary + rules)
|
|
68
|
+
to enable prompt caching across requests. Semantic hints are placed
|
|
69
|
+
in the user prompt instead.
|
|
70
|
+
|
|
68
71
|
Args:
|
|
69
72
|
vocabulary: List of valid short-form HED tags
|
|
70
73
|
extendable_tags: Tags that allow extension
|
|
71
|
-
semantic_hints: Optional semantic search results with relevant tags
|
|
72
74
|
no_extend: If True, prohibit tag extensions
|
|
73
75
|
|
|
74
76
|
Returns:
|
|
75
77
|
Complete system prompt with all HED rules
|
|
76
78
|
"""
|
|
77
|
-
return get_comprehensive_hed_guide(vocabulary, extendable_tags,
|
|
79
|
+
return get_comprehensive_hed_guide(vocabulary, extendable_tags, no_extend)
|
|
78
80
|
|
|
79
81
|
def _format_tag_suggestions(self, tag_suggestions: dict[str, list[str]]) -> str:
|
|
80
82
|
"""Format tag suggestions into a clear instruction block.
|
|
@@ -98,24 +100,46 @@ class AnnotationAgent:
|
|
|
98
100
|
)
|
|
99
101
|
return "\n".join(lines)
|
|
100
102
|
|
|
103
|
+
def _format_semantic_hints(self, semantic_hints: list[dict] | None) -> str:
|
|
104
|
+
"""Format semantic hints for inclusion in the user prompt.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
semantic_hints: List of hint dicts with tag, score, source keys
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Formatted hints section, or empty string if no hints
|
|
111
|
+
"""
|
|
112
|
+
if not semantic_hints:
|
|
113
|
+
return ""
|
|
114
|
+
|
|
115
|
+
logger.debug("Including %d semantic hints in user prompt", len(semantic_hints))
|
|
116
|
+
return "\n" + format_semantic_hints(semantic_hints)
|
|
117
|
+
|
|
101
118
|
def _build_user_prompt(
|
|
102
119
|
self,
|
|
103
120
|
description: str,
|
|
104
121
|
validation_errors: list[str] | None = None,
|
|
105
122
|
tag_suggestions: dict[str, list[str]] | None = None,
|
|
106
123
|
previous_annotation: str | None = None,
|
|
124
|
+
semantic_hints: list[dict] | None = None,
|
|
107
125
|
) -> str:
|
|
108
126
|
"""Build the user prompt for annotation.
|
|
109
127
|
|
|
128
|
+
Semantic hints are included here (not in system prompt) so the
|
|
129
|
+
system prompt stays static and cacheable across requests.
|
|
130
|
+
|
|
110
131
|
Args:
|
|
111
132
|
description: Natural language event description
|
|
112
133
|
validation_errors: Previous validation errors (if retrying)
|
|
113
134
|
tag_suggestions: LSP-suggested valid tags for invalid tags
|
|
114
135
|
previous_annotation: The previous annotation attempt (for targeted correction)
|
|
136
|
+
semantic_hints: Optional semantic search hints for relevant tags
|
|
115
137
|
|
|
116
138
|
Returns:
|
|
117
139
|
User prompt string
|
|
118
140
|
"""
|
|
141
|
+
hints_str = self._format_semantic_hints(semantic_hints)
|
|
142
|
+
|
|
119
143
|
if validation_errors:
|
|
120
144
|
errors_str = "\n".join(f"- {error}" for error in validation_errors)
|
|
121
145
|
suggestions_str = self._format_tag_suggestions(tag_suggestions or {})
|
|
@@ -133,12 +157,12 @@ class AnnotationAgent:
|
|
|
133
157
|
|
|
134
158
|
Fix these errors and generate a corrected HED annotation for:
|
|
135
159
|
{description}
|
|
136
|
-
|
|
160
|
+
{hints_str}
|
|
137
161
|
{replacement_note}CRITICAL: Output ONLY the raw HED annotation string."""
|
|
138
162
|
|
|
139
163
|
return f"""Generate a HED annotation for this event description:
|
|
140
164
|
{description}
|
|
141
|
-
|
|
165
|
+
{hints_str}
|
|
142
166
|
CRITICAL: Output ONLY the raw HED annotation string."""
|
|
143
167
|
|
|
144
168
|
async def annotate(self, state: HedAnnotationState) -> dict:
|
|
@@ -170,17 +194,16 @@ CRITICAL: Output ONLY the raw HED annotation string."""
|
|
|
170
194
|
# Use empty list - LLM will still generate valid annotations
|
|
171
195
|
extendable_tags = []
|
|
172
196
|
|
|
173
|
-
# Build
|
|
174
|
-
semantic_hints = state.get("semantic_hints", [])
|
|
197
|
+
# Build system prompt with HED rules (static per schema version for caching)
|
|
175
198
|
no_extend = state.get("no_extend", False)
|
|
176
199
|
system_prompt = self._build_system_prompt(
|
|
177
200
|
vocabulary,
|
|
178
201
|
extendable_tags,
|
|
179
|
-
semantic_hints if semantic_hints else None,
|
|
180
202
|
no_extend,
|
|
181
203
|
)
|
|
182
204
|
|
|
183
|
-
# Build user prompt with
|
|
205
|
+
# Build user prompt with feedback and semantic hints
|
|
206
|
+
semantic_hints = state.get("semantic_hints", [])
|
|
184
207
|
feedbacks = []
|
|
185
208
|
if state.get("validation_errors_augmented"):
|
|
186
209
|
feedbacks.extend(state["validation_errors_augmented"])
|
|
@@ -200,6 +223,7 @@ CRITICAL: Output ONLY the raw HED annotation string."""
|
|
|
200
223
|
feedbacks or None,
|
|
201
224
|
tag_suggestions or None,
|
|
202
225
|
previous_annotation,
|
|
226
|
+
semantic_hints if semantic_hints else None,
|
|
203
227
|
)
|
|
204
228
|
|
|
205
229
|
# Generate annotation
|
|
@@ -10,8 +10,8 @@ assembles them into the full system prompt.
|
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def
|
|
14
|
-
"""Format semantic hints for inclusion in the
|
|
13
|
+
def format_semantic_hints(hints: list[dict]) -> str:
|
|
14
|
+
"""Format semantic hints for inclusion in the user prompt.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
hints: List of semantic search results, each with:
|
|
@@ -21,7 +21,7 @@ def _format_semantic_hints(hints: list[dict]) -> str:
|
|
|
21
21
|
- prefix: Optional library prefix (e.g., "sc:")
|
|
22
22
|
|
|
23
23
|
Returns:
|
|
24
|
-
Formatted hints section for the
|
|
24
|
+
Formatted hints section for the user prompt
|
|
25
25
|
"""
|
|
26
26
|
if not hints:
|
|
27
27
|
return ""
|
|
@@ -33,6 +33,8 @@ def _format_semantic_hints(hints: list[dict]) -> str:
|
|
|
33
33
|
|
|
34
34
|
for hint in hints:
|
|
35
35
|
tag = hint.get("tag", "")
|
|
36
|
+
if not tag:
|
|
37
|
+
continue
|
|
36
38
|
prefix = hint.get("prefix", "")
|
|
37
39
|
score = hint.get("score", 0)
|
|
38
40
|
full_tag = f"{prefix}{tag}" if prefix else tag
|
|
@@ -45,7 +47,7 @@ def _format_semantic_hints(hints: list[dict]) -> str:
|
|
|
45
47
|
low_conf.append(full_tag)
|
|
46
48
|
|
|
47
49
|
lines = [
|
|
48
|
-
"##
|
|
50
|
+
"## SEMANTIC HINTS",
|
|
49
51
|
"",
|
|
50
52
|
"Based on your description, these schema tags may be relevant.",
|
|
51
53
|
"Note: this list may contain false positives - use your judgment.",
|
|
@@ -974,7 +976,6 @@ Just output the raw HED annotation string directly.
|
|
|
974
976
|
def get_comprehensive_hed_guide(
|
|
975
977
|
vocabulary_sample: list[str],
|
|
976
978
|
extendable_tags: list[str],
|
|
977
|
-
semantic_hints: list[dict] | None = None,
|
|
978
979
|
no_extend: bool = False,
|
|
979
980
|
) -> str:
|
|
980
981
|
"""Generate comprehensive HED annotation guide.
|
|
@@ -983,11 +984,13 @@ def get_comprehensive_hed_guide(
|
|
|
983
984
|
annotation agent. The guide includes vocabulary constraints, semantic
|
|
984
985
|
rules, correction workflows, and output format instructions.
|
|
985
986
|
|
|
987
|
+
Note: Semantic hints are NOT included here to keep the system prompt
|
|
988
|
+
static across requests, enabling prompt caching. Hints are passed
|
|
989
|
+
in the user prompt instead.
|
|
990
|
+
|
|
986
991
|
Args:
|
|
987
992
|
vocabulary_sample: Full list of valid HED tags (complete vocabulary)
|
|
988
993
|
extendable_tags: Tags that allow extension
|
|
989
|
-
semantic_hints: Optional list of semantically relevant tags from search
|
|
990
|
-
Each dict has: tag, score, source, prefix (optional)
|
|
991
994
|
no_extend: If True, add strict instructions to prohibit tag extensions
|
|
992
995
|
|
|
993
996
|
Returns:
|
|
@@ -997,16 +1000,23 @@ def get_comprehensive_hed_guide(
|
|
|
997
1000
|
extend_str = ", ".join(extendable_tags) if not no_extend else "(Extensions disabled)"
|
|
998
1001
|
|
|
999
1002
|
# Format optional sections
|
|
1000
|
-
hints_section = _format_semantic_hints(semantic_hints) if semantic_hints else ""
|
|
1001
1003
|
no_extend_warning = _build_no_extend_warning() if no_extend else ""
|
|
1002
1004
|
|
|
1003
1005
|
# Assemble guide from modular sections
|
|
1006
|
+
# Note: semantic hints are placed in the user prompt for cache efficiency
|
|
1004
1007
|
sections = [
|
|
1005
1008
|
"# HED ANNOTATION GUIDE\n",
|
|
1006
1009
|
no_extend_warning,
|
|
1007
1010
|
_build_vocabulary_check_section(),
|
|
1008
1011
|
_build_correction_workflow_section(),
|
|
1009
|
-
|
|
1012
|
+
(
|
|
1013
|
+
"## SEMANTIC HINTS\n\n"
|
|
1014
|
+
"The user message may include a SEMANTIC HINTS section with "
|
|
1015
|
+
"potentially relevant tags from schema search. If present, use "
|
|
1016
|
+
"these as guidance for tag selection, but verify each against "
|
|
1017
|
+
"the vocabulary. If no hints section is present, proceed without them.\n\n"
|
|
1018
|
+
"---\n\n"
|
|
1019
|
+
),
|
|
1010
1020
|
_build_semantic_rules_section(),
|
|
1011
1021
|
_build_relation_tags_section(),
|
|
1012
1022
|
_build_event_agent_section(),
|
|
@@ -193,6 +193,132 @@ class TestBuildUserPrompt:
|
|
|
193
193
|
|
|
194
194
|
assert "Previous annotation:" not in result
|
|
195
195
|
|
|
196
|
+
def test_first_pass_with_semantic_hints(self):
|
|
197
|
+
"""Semantic hints should appear in the user prompt on first pass."""
|
|
198
|
+
agent = self._make_agent()
|
|
199
|
+
result = agent._build_user_prompt(
|
|
200
|
+
"A dog chasing a cat",
|
|
201
|
+
semantic_hints=[
|
|
202
|
+
{"tag": "Animal-agent", "score": 0.9, "source": "hed-lsp"},
|
|
203
|
+
{"tag": "Chase", "score": 0.7, "source": "hed-lsp"},
|
|
204
|
+
],
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
assert "SEMANTIC HINTS" in result
|
|
208
|
+
assert "Animal-agent" in result
|
|
209
|
+
assert "Chase" in result
|
|
210
|
+
assert "A dog chasing a cat" in result
|
|
211
|
+
|
|
212
|
+
def test_correction_pass_with_semantic_hints(self):
|
|
213
|
+
"""Semantic hints should also appear in correction prompts."""
|
|
214
|
+
agent = self._make_agent()
|
|
215
|
+
result = agent._build_user_prompt(
|
|
216
|
+
"A dog chasing a cat",
|
|
217
|
+
validation_errors=["[TAG_INVALID] 'Chase' is not valid"],
|
|
218
|
+
semantic_hints=[
|
|
219
|
+
{"tag": "Animal-agent", "score": 0.9, "source": "hed-lsp"},
|
|
220
|
+
],
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
assert "SEMANTIC HINTS" in result
|
|
224
|
+
assert "Animal-agent" in result
|
|
225
|
+
assert "TAG_INVALID" in result
|
|
226
|
+
|
|
227
|
+
def test_no_hints_no_hints_section(self):
|
|
228
|
+
"""No hints should not add a hints section to the user prompt."""
|
|
229
|
+
agent = self._make_agent()
|
|
230
|
+
result = agent._build_user_prompt("A red circle")
|
|
231
|
+
|
|
232
|
+
assert "SEMANTIC HINTS" not in result
|
|
233
|
+
|
|
234
|
+
def test_empty_hints_no_hints_section(self):
|
|
235
|
+
"""Empty hints list should not add a hints section."""
|
|
236
|
+
agent = self._make_agent()
|
|
237
|
+
result = agent._build_user_prompt("A red circle", semantic_hints=[])
|
|
238
|
+
|
|
239
|
+
assert "SEMANTIC HINTS" not in result
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class TestFormatSemanticHints:
|
|
243
|
+
"""Tests for _format_semantic_hints method on AnnotationAgent."""
|
|
244
|
+
|
|
245
|
+
def _make_agent(self):
|
|
246
|
+
agent = object.__new__(AnnotationAgent)
|
|
247
|
+
return agent
|
|
248
|
+
|
|
249
|
+
def test_none_returns_empty(self):
|
|
250
|
+
"""None input should return empty string."""
|
|
251
|
+
agent = self._make_agent()
|
|
252
|
+
assert agent._format_semantic_hints(None) == ""
|
|
253
|
+
|
|
254
|
+
def test_empty_list_returns_empty(self):
|
|
255
|
+
"""Empty list should return empty string."""
|
|
256
|
+
agent = self._make_agent()
|
|
257
|
+
assert agent._format_semantic_hints([]) == ""
|
|
258
|
+
|
|
259
|
+
def test_valid_hints_returns_content(self):
|
|
260
|
+
"""Valid hints should produce formatted output."""
|
|
261
|
+
agent = self._make_agent()
|
|
262
|
+
result = agent._format_semantic_hints(
|
|
263
|
+
[
|
|
264
|
+
{"tag": "Red", "score": 0.9, "source": "hed-lsp"},
|
|
265
|
+
]
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
assert "SEMANTIC HINTS" in result
|
|
269
|
+
assert "Red" in result
|
|
270
|
+
|
|
271
|
+
def test_confidence_bucketing(self):
|
|
272
|
+
"""Hints should be categorized by confidence level."""
|
|
273
|
+
agent = self._make_agent()
|
|
274
|
+
result = agent._format_semantic_hints(
|
|
275
|
+
[
|
|
276
|
+
{"tag": "HighTag", "score": 0.95, "source": "hed-lsp"},
|
|
277
|
+
{"tag": "MedTag", "score": 0.6, "source": "hed-lsp"},
|
|
278
|
+
{"tag": "LowTag", "score": 0.3, "source": "hed-lsp"},
|
|
279
|
+
]
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
assert "High confidence" in result
|
|
283
|
+
assert "HighTag" in result
|
|
284
|
+
assert "Medium confidence" in result
|
|
285
|
+
assert "MedTag" in result
|
|
286
|
+
assert "Lower confidence" in result
|
|
287
|
+
assert "LowTag" in result
|
|
288
|
+
|
|
289
|
+
def test_skips_empty_tags(self):
|
|
290
|
+
"""Hints with empty tag should be skipped."""
|
|
291
|
+
agent = self._make_agent()
|
|
292
|
+
result = agent._format_semantic_hints(
|
|
293
|
+
[
|
|
294
|
+
{"tag": "", "score": 0.9, "source": "hed-lsp"},
|
|
295
|
+
{"tag": "ValidTag", "score": 0.8, "source": "hed-lsp"},
|
|
296
|
+
]
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
assert "ValidTag" in result
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
class TestSystemPromptCaching:
|
|
303
|
+
"""Tests that system prompt is static (no dynamic content) for caching."""
|
|
304
|
+
|
|
305
|
+
def test_system_prompt_has_hints_pointer_not_content(self):
|
|
306
|
+
"""System prompt should reference hints but not contain actual hint data."""
|
|
307
|
+
from src.utils.hed_comprehensive_guide import get_comprehensive_hed_guide
|
|
308
|
+
|
|
309
|
+
guide = get_comprehensive_hed_guide(
|
|
310
|
+
vocabulary_sample=["Red", "Circle"],
|
|
311
|
+
extendable_tags=["Animal"],
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Should have the pointer section
|
|
315
|
+
assert "## SEMANTIC HINTS" in guide
|
|
316
|
+
assert "may include" in guide
|
|
317
|
+
# Should NOT contain dynamic hint content
|
|
318
|
+
assert "High confidence" not in guide
|
|
319
|
+
assert "Medium confidence" not in guide
|
|
320
|
+
assert "Lower confidence" not in guide
|
|
321
|
+
|
|
196
322
|
|
|
197
323
|
class TestPromptSections:
|
|
198
324
|
"""Tests for prompt structure and sections."""
|
|
@@ -45,38 +45,24 @@ class TestComprehensiveGuide:
|
|
|
45
45
|
# Should show extensions as disabled
|
|
46
46
|
assert "(Extensions disabled)" in guide
|
|
47
47
|
|
|
48
|
-
def
|
|
49
|
-
"""Test guide
|
|
48
|
+
def test_guide_has_semantic_hints_pointer(self):
|
|
49
|
+
"""Test guide includes a pointer to check user message for semantic hints."""
|
|
50
50
|
vocabulary = ["Event", "Reward", "Animal-agent"]
|
|
51
51
|
extendable_tags = ["Label"]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
assert "POTENTIALLY RELEVANT TAGS" in guide
|
|
62
|
-
assert "Reward" in guide
|
|
63
|
-
assert "Animal-agent" in guide
|
|
64
|
-
# Check confidence indicators
|
|
65
|
-
assert "high" in guide.lower() or "0.95" in guide
|
|
66
|
-
|
|
67
|
-
def test_guide_with_semantic_hints_and_no_extend(self):
|
|
68
|
-
"""Test guide with both semantic hints and no_extend."""
|
|
52
|
+
|
|
53
|
+
guide = get_comprehensive_hed_guide(vocabulary, extendable_tags)
|
|
54
|
+
|
|
55
|
+
# System prompt should point to user message for hints (not contain them)
|
|
56
|
+
assert "SEMANTIC HINTS" in guide
|
|
57
|
+
assert "user message" in guide.lower()
|
|
58
|
+
|
|
59
|
+
def test_guide_no_extend_with_hints_pointer(self):
|
|
60
|
+
"""Test guide with no_extend has both hints pointer and extension warning."""
|
|
69
61
|
vocabulary = ["Event", "Visual-presentation"]
|
|
70
62
|
extendable_tags = ["Label"]
|
|
71
|
-
semantic_hints = [
|
|
72
|
-
{"tag": "Visual-presentation", "prefix": "", "score": 0.9, "source": "keyword"},
|
|
73
|
-
]
|
|
74
63
|
|
|
75
|
-
guide = get_comprehensive_hed_guide(
|
|
76
|
-
vocabulary, extendable_tags, semantic_hints=semantic_hints, no_extend=True
|
|
77
|
-
)
|
|
64
|
+
guide = get_comprehensive_hed_guide(vocabulary, extendable_tags, no_extend=True)
|
|
78
65
|
|
|
79
|
-
|
|
80
|
-
assert "POTENTIALLY RELEVANT TAGS" in guide
|
|
66
|
+
assert "SEMANTIC HINTS" in guide
|
|
81
67
|
assert "EXTENSIONS STRICTLY PROHIBITED" in guide
|
|
82
68
|
assert "(Extensions disabled)" in guide
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|