Skip to content

Commit 8b0e346

Browse files
refactor: schema-driven prompting via Pydantic JSON Schema
Refactors prompting to be JSON-Schema-first. prompt_for_schema() now uses Pydantic model_json_schema() and tool schemas and Pydantic models go through the same prompting path. Removes the custom x-* hint fields and replaces them with schema-based prompting heuristics (including array-of-strings CSV input and optional object include gating). Fixes default rendering to distinguish “default key present” vs absent and renders defaults via json.dumps. Adds validate_schema() for early JSON Schema validation, updates tools execute to fail fast on empty IDs and to accept string-encoded schemas when they decode to an object, and restores recursive dict key-type validation for nested Pydantic models. Updates tests accordingly and adjusts interrogate exclusions for generated cforge/_version.py. Cleans up .gitignore duplicates.
1 parent d5e2042 commit 8b0e346

9 files changed

Lines changed: 393 additions & 187 deletions

File tree

.gitignore

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ CLAUDE.local.md
1818
.scannerwork
1919
llms-full.txt
2020
aider*
21-
.aider*
2221
todo/
2322
*.sarif
2423
devskim-results.sarif
@@ -35,7 +34,6 @@ token.txt
3534
mcpgateway.sbom.xml
3635
gateway_service_leader.lock
3736
docs/docs/test/
38-
tmp
3937
*.tgz
4038
*.gz
4139
*.bz
@@ -74,9 +72,7 @@ dictionary.dic
7472
pdm.lock
7573
.pdm-python
7674
temp/
77-
public/
7875
*history.md
79-
htmlcov
8076
test_commands.md
8177
cover.md
8278
build/
@@ -99,28 +95,22 @@ scribeflow.log
9995
coverage_re
10096
bin/flagged
10197
flagged/
102-
certs/
10398
# VENV
10499
.python37/
105100
.python39/
106101

107102
# Byte-compiled / optimized / DLL files
108103
__pycache__/
109-
**/__pycache__/
110104
*.py[cod]
111105
*$py.class
112106
mcpgateway-wrapper/src/mcp_gateway_wrapper/__pycache__/
113107

114-
# Bak
115-
*.bak
116-
117108
# C extensions
118109
*.so
119110

120111
# Distribution / packaging
121112
.wily/
122113
.Python
123-
build/
124114
develop-eggs/
125115
dist/
126116
downloads/
@@ -165,7 +155,6 @@ coverage.xml
165155
*.pot
166156

167157
# Django stuff:
168-
*.log
169158
local_settings.py
170159
db.sqlite3
171160

@@ -199,8 +188,6 @@ celerybeat-schedule
199188
*.sage.py
200189

201190
# Environments
202-
.env
203-
.venv
204191
env/
205192
venv/
206193
ENV/
@@ -231,9 +218,6 @@ dmypy.json
231218

232219
.idea/
233220

234-
# Sonar
235-
.scannerwork
236-
237221
# vim
238222
*.swp
239223
*,cover
@@ -244,9 +228,6 @@ logging/
244228

245229
.ai*
246230

247-
# downloads
248-
downloads/
249-
250231
# db_path
251232
db_path/
252233

cforge/commands/resources/plugins.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
to support them.
1414
"""
1515

16+
# Standard
1617
from typing import Any, Dict, Optional
1718

19+
# Third-Party
1820
import typer
1921

22+
# First-Party
2023
from cforge.common.console import get_console
2124
from cforge.common.errors import AuthenticationError, CaseInsensitiveEnum, CLIError, handle_exception
2225
from cforge.common.http import make_authenticated_request

cforge/commands/resources/tools.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,10 @@ def tools_execute(
186186
console = get_console()
187187

188188
try:
189+
if not tool_id.strip():
190+
raise CLIError("Tool ID must be a non-empty string")
191+
189192
prefilled_data: Optional[Dict[str, Any]] = None
190-
prompt_optional = True
191193
if data_file:
192194
if not data_file.exists():
193195
console.print(f"[red]File not found: {data_file}[/red]")
@@ -196,7 +198,7 @@ def tools_execute(
196198
if not isinstance(file_data, dict):
197199
raise CLIError("Data file must contain a JSON object")
198200
prefilled_data = file_data
199-
prompt_optional = False
201+
prompt_optional = prefilled_data is None
200202

201203
tool_result = make_authenticated_request("GET", f"/tools/{tool_id}")
202204
assert isinstance(tool_result, dict)
@@ -212,6 +214,14 @@ def tools_execute(
212214
input_schema = {"type": "object", "properties": {}}
213215
elif isinstance(raw_schema, dict):
214216
input_schema = raw_schema
217+
elif isinstance(raw_schema, str):
218+
try:
219+
parsed_schema = json.loads(raw_schema)
220+
except json.JSONDecodeError as exc:
221+
raise CLIError("Tool input schema must be a JSON object") from exc
222+
if not isinstance(parsed_schema, dict):
223+
raise CLIError("Tool input schema must be a JSON object")
224+
input_schema = parsed_schema
215225
else:
216226
raise CLIError("Tool input schema must be a JSON object")
217227

0 commit comments

Comments
 (0)