63 lines
3.9 KiB
Python
63 lines
3.9 KiB
Python
"""Versioned tool shapes and value-free repair instructions, no query rewriting."""
|
|
import copy
|
|
import re
|
|
|
|
HEADER = '\nTool argument contracts v1 (plain JSON output):\n'
|
|
FILTER_REQUIREMENT = ('filters must be a nonempty array of objects: '
|
|
'[{"column":"observed_column","operator":"eq","value":123}]; '
|
|
'use verified values, never a flat list.')
|
|
|
|
|
|
def finding_repair_instructions(context):
|
|
detail=context.get('previous_rejection_details',{})
|
|
if (not context.get('bootstrap_json_framing') or detail.get('expected_types')!=['object']
|
|
or not re.fullmatch(r'/update/working_brief/findings/[0-9]+',detail.get('validation_path',''))):
|
|
return ''
|
|
return ('\nFinding type repair: each findings element must be a JSON OBJECT, not a string, '
|
|
'array, tuple or encoded record. Shape example: '
|
|
'{"id":"B0001","direction":"neutral","text":"Scoped observation",'
|
|
'"scope":"Examined snapshot","evidence_refs":["E000001"]}. '
|
|
'These are illustrative values: preserve existing B IDs and use only actual visible E IDs '
|
|
'and evidence-grounded text/scope. Do not copy invented evidence or erase findings to bypass validation. '
|
|
'No $record/$table encoding in output. Repair the response without repeating research.\n')
|
|
|
|
|
|
def filter_feedback(details):
|
|
result = copy.deepcopy(details)
|
|
if re.fullmatch(r'/next/arguments/filters(?:/[0-9]+)?', result.get('validation_path', '')):
|
|
result['requirement'] = FILTER_REQUIREMENT
|
|
return result
|
|
|
|
|
|
def instructions(context):
|
|
if context.get('tool_guidance_version') != 1:
|
|
return ''
|
|
from .investigation_compact import preparing_review
|
|
if preparing_review(context) or context.get('lean_session', {}).get('checkpoint'):
|
|
return ''
|
|
bindings = context['tool_schema'].get('x-project-bindings', {})
|
|
text = (HEADER + 'Use operation="project.query" for these arguments. Prefer a matching larger tool '
|
|
'before separate schema/search/read calls; primitive tools remain available.\n')
|
|
if bindings.get('relation_profiles'):
|
|
text += ('database.relations arguments: {"kind":"database.relations",'
|
|
'"profile":"NAME_FROM_relation_profiles","value":123}. '
|
|
'Select the profile from the catalog and replace 123 with the observed start key '
|
|
'(integer for numeric IDs, string for textual keys). No table or SQL argument.\n')
|
|
if bindings.get('source_analysis'):
|
|
text += ('source.context arguments: {"kind":"source.context",'
|
|
'"text":"SYMBOL_OR_IDENTIFIER_FROM_EVIDENCE","path_prefix":""}. '
|
|
'Replace text with the actual term; empty path_prefix means ')
|
|
text += ('the configured scope prefixes. ' if bindings['source_analysis'].get('version')==2 else 'repository root. ')
|
|
text += 'No registration, offset, limit or command argument.\n'
|
|
if bindings['source_analysis'].get('version')==2:
|
|
text += ('source.context v2 optionally accepts entity_refs:["N0001"] from supplied case entities '
|
|
'and scope:"code"|"schema"|"historical_sql" (default code). Grounded names and scripts '
|
|
'are added by the controller; numeric coincidences are weak summaries, not identities. '
|
|
'Ranking is heuristic; supporting and other-location candidates may matter.\n')
|
|
text += ('For primitive database.rows/database.count, filters MUST be a nonempty array of objects, '
|
|
'for example [{"column":"observed_column","operator":"eq","value":123}]. '
|
|
'Replace the illustrative column/value with schema-verified, case-grounded inputs. '
|
|
'Never emit [] or flatten an object into ["column",...,"operator",...,"value",...]. '
|
|
'Input compression encodings do not apply to output: return ordinary JSON objects.\n')
|
|
return text
|