ostruct-cli 0.7.1__py3-none-any.whl → 0.8.0__py3-none-any.whl
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.
- ostruct/cli/__init__.py +21 -3
- ostruct/cli/base_errors.py +1 -1
- ostruct/cli/cli.py +66 -1983
- ostruct/cli/click_options.py +460 -28
- ostruct/cli/code_interpreter.py +238 -0
- ostruct/cli/commands/__init__.py +32 -0
- ostruct/cli/commands/list_models.py +128 -0
- ostruct/cli/commands/quick_ref.py +50 -0
- ostruct/cli/commands/run.py +137 -0
- ostruct/cli/commands/update_registry.py +71 -0
- ostruct/cli/config.py +277 -0
- ostruct/cli/cost_estimation.py +134 -0
- ostruct/cli/errors.py +310 -6
- ostruct/cli/exit_codes.py +1 -0
- ostruct/cli/explicit_file_processor.py +548 -0
- ostruct/cli/field_utils.py +69 -0
- ostruct/cli/file_info.py +42 -9
- ostruct/cli/file_list.py +301 -102
- ostruct/cli/file_search.py +455 -0
- ostruct/cli/file_utils.py +47 -13
- ostruct/cli/mcp_integration.py +541 -0
- ostruct/cli/model_creation.py +150 -1
- ostruct/cli/model_validation.py +204 -0
- ostruct/cli/progress_reporting.py +398 -0
- ostruct/cli/registry_updates.py +14 -9
- ostruct/cli/runner.py +1418 -0
- ostruct/cli/schema_utils.py +113 -0
- ostruct/cli/services.py +626 -0
- ostruct/cli/template_debug.py +748 -0
- ostruct/cli/template_debug_help.py +162 -0
- ostruct/cli/template_env.py +15 -6
- ostruct/cli/template_filters.py +55 -3
- ostruct/cli/template_optimizer.py +474 -0
- ostruct/cli/template_processor.py +1080 -0
- ostruct/cli/template_rendering.py +69 -34
- ostruct/cli/token_validation.py +286 -0
- ostruct/cli/types.py +78 -0
- ostruct/cli/unattended_operation.py +269 -0
- ostruct/cli/validators.py +386 -3
- {ostruct_cli-0.7.1.dist-info → ostruct_cli-0.8.0.dist-info}/LICENSE +2 -0
- ostruct_cli-0.8.0.dist-info/METADATA +633 -0
- ostruct_cli-0.8.0.dist-info/RECORD +69 -0
- {ostruct_cli-0.7.1.dist-info → ostruct_cli-0.8.0.dist-info}/WHEEL +1 -1
- ostruct_cli-0.7.1.dist-info/METADATA +0 -369
- ostruct_cli-0.7.1.dist-info/RECORD +0 -45
- {ostruct_cli-0.7.1.dist-info → ostruct_cli-0.8.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,162 @@
|
|
1
|
+
"""Template debugging help system for ostruct CLI.
|
2
|
+
|
3
|
+
This module provides comprehensive help and examples for template debugging features.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import click
|
7
|
+
|
8
|
+
TEMPLATE_DEBUG_HELP = """
|
9
|
+
🐛 Template Debugging Quick Reference
|
10
|
+
|
11
|
+
BASIC DEBUGGING:
|
12
|
+
--debug 🐛 Enable all debug output (verbose logging + template expansion)
|
13
|
+
--show-templates 📝 Show expanded templates only (clean output)
|
14
|
+
--show-context 📋 Show template variables summary
|
15
|
+
--show-context-detailed 📋 Show detailed variable context with content preview
|
16
|
+
--debug-templates 🔍 Enable step-by-step template expansion analysis
|
17
|
+
|
18
|
+
OPTIMIZATION DEBUGGING:
|
19
|
+
--show-pre-optimization 🔧 Show template before optimization
|
20
|
+
--show-optimization-diff 🔄 Show optimization changes (before/after comparison)
|
21
|
+
--show-optimization-steps 🔧 Show detailed optimization step tracking
|
22
|
+
--optimization-step-detail [summary|detailed] 📊 Control step detail level
|
23
|
+
--no-optimization ⚡ Skip optimization entirely for debugging
|
24
|
+
|
25
|
+
PERFORMANCE ANALYSIS:
|
26
|
+
--profile-template ⏱️ Show template performance breakdown (future)
|
27
|
+
--analyze-optimization 📊 Show optimization impact analysis (future)
|
28
|
+
|
29
|
+
INTERACTIVE DEBUGGING:
|
30
|
+
ostruct debug template.j2 schema.json --debug-shell 🎯 Interactive debug shell (future)
|
31
|
+
|
32
|
+
EXAMPLES:
|
33
|
+
|
34
|
+
🔍 Basic Template Debugging:
|
35
|
+
# Show everything (most verbose)
|
36
|
+
ostruct run template.j2 schema.json --debug -ft config.yaml
|
37
|
+
|
38
|
+
# Just template content (clean output)
|
39
|
+
ostruct run template.j2 schema.json --show-templates -ft config.yaml
|
40
|
+
|
41
|
+
# Show template variables and context
|
42
|
+
ostruct run template.j2 schema.json --show-context -ft config.yaml
|
43
|
+
|
44
|
+
🔧 Optimization Debugging:
|
45
|
+
# See template before optimization
|
46
|
+
ostruct run template.j2 schema.json --show-pre-optimization -ft config.yaml
|
47
|
+
|
48
|
+
# See what optimization changed
|
49
|
+
ostruct run template.j2 schema.json --show-optimization-diff -ft config.yaml
|
50
|
+
|
51
|
+
# See step-by-step optimization process
|
52
|
+
ostruct run template.j2 schema.json --show-optimization-steps -ft config.yaml
|
53
|
+
|
54
|
+
# Detailed optimization steps with full diffs
|
55
|
+
ostruct run template.j2 schema.json --show-optimization-steps --optimization-step-detail detailed -ft config.yaml
|
56
|
+
|
57
|
+
# Skip optimization entirely
|
58
|
+
ostruct run template.j2 schema.json --no-optimization -ft config.yaml
|
59
|
+
|
60
|
+
🎯 Combined Debugging:
|
61
|
+
# Show both optimization diff and steps
|
62
|
+
ostruct run template.j2 schema.json --show-optimization-diff --show-optimization-steps -ft config.yaml
|
63
|
+
|
64
|
+
# Full debugging with context and optimization
|
65
|
+
ostruct run template.j2 schema.json --debug --show-context --show-optimization-diff -ft config.yaml
|
66
|
+
|
67
|
+
🚨 Troubleshooting Common Issues:
|
68
|
+
|
69
|
+
❌ Undefined Variable Errors:
|
70
|
+
Problem: UndefinedError: 'variable_name' is undefined
|
71
|
+
Solution: Use --show-context to see available variables
|
72
|
+
Example: ostruct run template.j2 schema.json --show-context -ft config.yaml
|
73
|
+
|
74
|
+
❌ Template Not Expanding:
|
75
|
+
Problem: Template appears unchanged in output
|
76
|
+
Solution: Use --show-templates to see expansion
|
77
|
+
Example: ostruct run template.j2 schema.json --show-templates -ft config.yaml
|
78
|
+
|
79
|
+
❌ Optimization Breaking Template:
|
80
|
+
Problem: Template works without optimization but fails with it
|
81
|
+
Solution: Use --show-optimization-diff to see changes
|
82
|
+
Example: ostruct run template.j2 schema.json --show-optimization-diff -ft config.yaml
|
83
|
+
|
84
|
+
❌ Performance Issues:
|
85
|
+
Problem: Template rendering is slow
|
86
|
+
Solution: Use --show-optimization-steps to see bottlenecks
|
87
|
+
Example: ostruct run template.j2 schema.json --show-optimization-steps -ft config.yaml
|
88
|
+
|
89
|
+
💡 Pro Tips:
|
90
|
+
• Use --dry-run with debugging flags to avoid API calls
|
91
|
+
• Combine multiple debug flags for comprehensive analysis
|
92
|
+
• Start with --show-templates for basic template issues
|
93
|
+
• Use --debug for full diagnostic information
|
94
|
+
• Use --show-context when variables are undefined
|
95
|
+
• Use optimization debugging when templates work but optimization fails
|
96
|
+
|
97
|
+
📚 For more information, see: docs/template_debugging.md
|
98
|
+
"""
|
99
|
+
|
100
|
+
|
101
|
+
def show_template_debug_help() -> None:
|
102
|
+
"""Display comprehensive template debugging help."""
|
103
|
+
click.echo(TEMPLATE_DEBUG_HELP, err=True)
|
104
|
+
|
105
|
+
|
106
|
+
def show_quick_debug_tips() -> None:
|
107
|
+
"""Show quick debugging tips for common issues."""
|
108
|
+
quick_tips = """
|
109
|
+
🚀 Quick Debug Tips:
|
110
|
+
|
111
|
+
Template not working? Try:
|
112
|
+
1. ostruct run template.j2 schema.json --show-templates --dry-run
|
113
|
+
2. ostruct run template.j2 schema.json --show-context --dry-run
|
114
|
+
3. ostruct run template.j2 schema.json --debug --dry-run
|
115
|
+
|
116
|
+
Optimization issues? Try:
|
117
|
+
1. ostruct run template.j2 schema.json --show-optimization-diff --dry-run
|
118
|
+
2. ostruct run template.j2 schema.json --no-optimization --dry-run
|
119
|
+
|
120
|
+
For full help: ostruct run --help-debug
|
121
|
+
"""
|
122
|
+
click.echo(quick_tips, err=True)
|
123
|
+
|
124
|
+
|
125
|
+
def show_debug_examples() -> None:
|
126
|
+
"""Show practical debugging examples."""
|
127
|
+
examples = """
|
128
|
+
🎯 Template Debugging Examples:
|
129
|
+
|
130
|
+
📝 Basic Template Issues:
|
131
|
+
# Check if template expands correctly
|
132
|
+
ostruct run my_template.j2 schema.json --show-templates --dry-run -ft config.yaml
|
133
|
+
|
134
|
+
# See what variables are available
|
135
|
+
ostruct run my_template.j2 schema.json --show-context --dry-run -ft config.yaml
|
136
|
+
|
137
|
+
# Full debug output
|
138
|
+
ostruct run my_template.j2 schema.json --debug --dry-run -ft config.yaml
|
139
|
+
|
140
|
+
🔧 Optimization Issues:
|
141
|
+
# See what optimization does to your template
|
142
|
+
ostruct run my_template.j2 schema.json --show-optimization-diff --dry-run -ft config.yaml
|
143
|
+
|
144
|
+
# Track each optimization step
|
145
|
+
ostruct run my_template.j2 schema.json --show-optimization-steps --dry-run -ft config.yaml
|
146
|
+
|
147
|
+
# Bypass optimization entirely
|
148
|
+
ostruct run my_template.j2 schema.json --no-optimization --dry-run -ft config.yaml
|
149
|
+
|
150
|
+
🔍 Advanced Debugging:
|
151
|
+
# Combine multiple debug features
|
152
|
+
ostruct run my_template.j2 schema.json \\
|
153
|
+
--debug \\
|
154
|
+
--show-context \\
|
155
|
+
--show-optimization-diff \\
|
156
|
+
--show-optimization-steps \\
|
157
|
+
--dry-run \\
|
158
|
+
-ft config.yaml
|
159
|
+
|
160
|
+
💡 Remember: Always use --dry-run when debugging to avoid API calls!
|
161
|
+
"""
|
162
|
+
click.echo(examples, err=True)
|
ostruct/cli/template_env.py
CHANGED
@@ -3,10 +3,11 @@
|
|
3
3
|
This module provides a centralized factory for creating consistently configured Jinja2 environments.
|
4
4
|
"""
|
5
5
|
|
6
|
-
from typing import Optional, Type
|
6
|
+
from typing import List, Optional, Type, Union
|
7
7
|
|
8
8
|
import jinja2
|
9
9
|
from jinja2 import Environment
|
10
|
+
from jinja2.ext import Extension
|
10
11
|
|
11
12
|
from .template_extensions import CommentExtension
|
12
13
|
from .template_filters import register_template_filters
|
@@ -17,6 +18,7 @@ def create_jinja_env(
|
|
17
18
|
undefined: Optional[Type[jinja2.Undefined]] = None,
|
18
19
|
loader: Optional[jinja2.BaseLoader] = None,
|
19
20
|
validation_mode: bool = False,
|
21
|
+
debug_mode: bool = False,
|
20
22
|
) -> Environment:
|
21
23
|
"""Create a consistently configured Jinja2 environment.
|
22
24
|
|
@@ -24,6 +26,7 @@ def create_jinja_env(
|
|
24
26
|
undefined: Custom undefined class to use. Defaults to StrictUndefined.
|
25
27
|
loader: Template loader to use. Defaults to None.
|
26
28
|
validation_mode: Whether to configure the environment for validation (uses SafeUndefined).
|
29
|
+
debug_mode: Whether to enable debug features like undefined variable detection.
|
27
30
|
|
28
31
|
Returns:
|
29
32
|
A configured Jinja2 environment.
|
@@ -35,6 +38,16 @@ def create_jinja_env(
|
|
35
38
|
elif undefined is None:
|
36
39
|
undefined = jinja2.StrictUndefined
|
37
40
|
|
41
|
+
# Configure extensions based on debug mode
|
42
|
+
extensions: List[Union[str, Type[Extension]]] = [
|
43
|
+
"jinja2.ext.do",
|
44
|
+
"jinja2.ext.loopcontrols",
|
45
|
+
CommentExtension,
|
46
|
+
]
|
47
|
+
|
48
|
+
if debug_mode:
|
49
|
+
extensions.append("jinja2.ext.debug") # Enable {% debug %} tag
|
50
|
+
|
38
51
|
env = Environment(
|
39
52
|
loader=loader,
|
40
53
|
undefined=undefined,
|
@@ -42,11 +55,7 @@ def create_jinja_env(
|
|
42
55
|
trim_blocks=True,
|
43
56
|
lstrip_blocks=True,
|
44
57
|
keep_trailing_newline=True,
|
45
|
-
extensions=
|
46
|
-
"jinja2.ext.do",
|
47
|
-
"jinja2.ext.loopcontrols",
|
48
|
-
CommentExtension,
|
49
|
-
],
|
58
|
+
extensions=extensions,
|
50
59
|
)
|
51
60
|
|
52
61
|
# Register all template filters
|
ostruct/cli/template_filters.py
CHANGED
@@ -7,15 +7,27 @@ import logging
|
|
7
7
|
import re
|
8
8
|
import textwrap
|
9
9
|
from collections import Counter
|
10
|
-
from typing import
|
10
|
+
from typing import (
|
11
|
+
TYPE_CHECKING,
|
12
|
+
Any,
|
13
|
+
Dict,
|
14
|
+
List,
|
15
|
+
Optional,
|
16
|
+
Sequence,
|
17
|
+
TypeVar,
|
18
|
+
Union,
|
19
|
+
)
|
11
20
|
|
12
21
|
import tiktoken
|
13
|
-
from jinja2 import Environment, pass_context
|
22
|
+
from jinja2 import Environment, TemplateRuntimeError, pass_context
|
14
23
|
from pygments import highlight
|
15
24
|
from pygments.formatters import HtmlFormatter, NullFormatter, TerminalFormatter
|
16
25
|
from pygments.lexers import TextLexer, get_lexer_by_name, guess_lexer
|
17
26
|
from pygments.util import ClassNotFound
|
18
27
|
|
28
|
+
if TYPE_CHECKING:
|
29
|
+
pass
|
30
|
+
|
19
31
|
logger = logging.getLogger(__name__)
|
20
32
|
|
21
33
|
T = TypeVar("T")
|
@@ -127,7 +139,7 @@ def list_to_table(
|
|
127
139
|
"""Convert list to markdown table."""
|
128
140
|
if not headers:
|
129
141
|
return "| # | Value |\n| --- | --- |\n" + "\n".join(
|
130
|
-
f"| {i+1} | {item} |" for i, item in enumerate(items)
|
142
|
+
f"| {i + 1} | {item} |" for i, item in enumerate(items)
|
131
143
|
)
|
132
144
|
return (
|
133
145
|
f"| {' | '.join(headers)} |\n| {' | '.join('-' * len(h) for h in headers)} |\n"
|
@@ -591,6 +603,44 @@ def format_code(
|
|
591
603
|
return str(text)
|
592
604
|
|
593
605
|
|
606
|
+
def single_filter(value: Any) -> Any:
|
607
|
+
"""Extract single item from a list, ensuring exactly one item exists."""
|
608
|
+
# Import here to avoid circular imports
|
609
|
+
try:
|
610
|
+
from .file_list import FileInfoList
|
611
|
+
|
612
|
+
FileInfoListType = FileInfoList
|
613
|
+
except ImportError:
|
614
|
+
# Fallback if imports fail
|
615
|
+
FileInfoListType = None
|
616
|
+
|
617
|
+
if FileInfoListType and isinstance(value, FileInfoListType):
|
618
|
+
var_alias = getattr(value, "_var_alias", None) or "list"
|
619
|
+
if len(value) == 1:
|
620
|
+
return value[0] # ✅ Return the FileInfo object, not the list
|
621
|
+
else:
|
622
|
+
raise TemplateRuntimeError(
|
623
|
+
f"Filter 'single' expected exactly 1 file for '{var_alias}', got {len(value)}."
|
624
|
+
)
|
625
|
+
|
626
|
+
# For other list types (but not strings or dicts)
|
627
|
+
if (
|
628
|
+
hasattr(value, "__len__")
|
629
|
+
and hasattr(value, "__getitem__")
|
630
|
+
and not isinstance(value, (str, dict))
|
631
|
+
and hasattr(value, "__iter__")
|
632
|
+
):
|
633
|
+
if len(value) == 1:
|
634
|
+
return value[0]
|
635
|
+
else:
|
636
|
+
raise TemplateRuntimeError(
|
637
|
+
f"Filter 'single' expected exactly 1 item, got {len(value)}."
|
638
|
+
)
|
639
|
+
|
640
|
+
# Pass through non-list types (including FileInfo and strings)
|
641
|
+
return value
|
642
|
+
|
643
|
+
|
594
644
|
def register_template_filters(env: Environment) -> None:
|
595
645
|
"""Register all template filters with the Jinja2 environment.
|
596
646
|
|
@@ -630,6 +680,8 @@ def register_template_filters(env: Environment) -> None:
|
|
630
680
|
"escape_special": escape_special,
|
631
681
|
# Table utilities
|
632
682
|
"auto_table": auto_table,
|
683
|
+
# Single item extraction
|
684
|
+
"single": single_filter,
|
633
685
|
}
|
634
686
|
|
635
687
|
env.filters.update(filters)
|