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.
Files changed (46) hide show
  1. ostruct/cli/__init__.py +21 -3
  2. ostruct/cli/base_errors.py +1 -1
  3. ostruct/cli/cli.py +66 -1983
  4. ostruct/cli/click_options.py +460 -28
  5. ostruct/cli/code_interpreter.py +238 -0
  6. ostruct/cli/commands/__init__.py +32 -0
  7. ostruct/cli/commands/list_models.py +128 -0
  8. ostruct/cli/commands/quick_ref.py +50 -0
  9. ostruct/cli/commands/run.py +137 -0
  10. ostruct/cli/commands/update_registry.py +71 -0
  11. ostruct/cli/config.py +277 -0
  12. ostruct/cli/cost_estimation.py +134 -0
  13. ostruct/cli/errors.py +310 -6
  14. ostruct/cli/exit_codes.py +1 -0
  15. ostruct/cli/explicit_file_processor.py +548 -0
  16. ostruct/cli/field_utils.py +69 -0
  17. ostruct/cli/file_info.py +42 -9
  18. ostruct/cli/file_list.py +301 -102
  19. ostruct/cli/file_search.py +455 -0
  20. ostruct/cli/file_utils.py +47 -13
  21. ostruct/cli/mcp_integration.py +541 -0
  22. ostruct/cli/model_creation.py +150 -1
  23. ostruct/cli/model_validation.py +204 -0
  24. ostruct/cli/progress_reporting.py +398 -0
  25. ostruct/cli/registry_updates.py +14 -9
  26. ostruct/cli/runner.py +1418 -0
  27. ostruct/cli/schema_utils.py +113 -0
  28. ostruct/cli/services.py +626 -0
  29. ostruct/cli/template_debug.py +748 -0
  30. ostruct/cli/template_debug_help.py +162 -0
  31. ostruct/cli/template_env.py +15 -6
  32. ostruct/cli/template_filters.py +55 -3
  33. ostruct/cli/template_optimizer.py +474 -0
  34. ostruct/cli/template_processor.py +1080 -0
  35. ostruct/cli/template_rendering.py +69 -34
  36. ostruct/cli/token_validation.py +286 -0
  37. ostruct/cli/types.py +78 -0
  38. ostruct/cli/unattended_operation.py +269 -0
  39. ostruct/cli/validators.py +386 -3
  40. {ostruct_cli-0.7.1.dist-info → ostruct_cli-0.8.0.dist-info}/LICENSE +2 -0
  41. ostruct_cli-0.8.0.dist-info/METADATA +633 -0
  42. ostruct_cli-0.8.0.dist-info/RECORD +69 -0
  43. {ostruct_cli-0.7.1.dist-info → ostruct_cli-0.8.0.dist-info}/WHEEL +1 -1
  44. ostruct_cli-0.7.1.dist-info/METADATA +0 -369
  45. ostruct_cli-0.7.1.dist-info/RECORD +0 -45
  46. {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)
@@ -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
@@ -7,15 +7,27 @@ import logging
7
7
  import re
8
8
  import textwrap
9
9
  from collections import Counter
10
- from typing import Any, Dict, List, Optional, Sequence, TypeVar, Union
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)