22
33import json
44import sys
5+ from datetime import datetime
56from pathlib import Path
67from typing import Optional
78
1920}
2021
2122
23+ def save_extraction_result (
24+ result_data : dict ,
25+ template_name : str ,
26+ output_path : Optional [str ] = None ,
27+ data_dir : str = "data"
28+ ) -> str :
29+ """Save extraction result to file and return the path."""
30+ if output_path :
31+ # Use specified path
32+ save_path = Path (output_path )
33+ else :
34+ # Generate automatic path in data directory
35+ timestamp = datetime .now ().strftime ("%Y%m%d_%H%M%S" )
36+ filename = f"{ template_name } _extraction_{ timestamp } .json"
37+
38+ data_path = Path (data_dir )
39+ data_path .mkdir (exist_ok = True )
40+ save_path = data_path / filename
41+
42+ # Ensure parent directory exists
43+ save_path .parent .mkdir (parents = True , exist_ok = True )
44+
45+ # Save the file
46+ save_path .write_text (
47+ json .dumps (result_data , indent = 2 , ensure_ascii = False ),
48+ encoding = "utf-8"
49+ )
50+
51+ return str (save_path )
52+
53+
2254@click .group ()
2355@click .option ("--debug" , is_flag = True , help = "Enable debug logging" )
2456@click .pass_context
@@ -47,16 +79,20 @@ def list_templates() -> None:
4779@click .argument ("template" , type = click .Choice (list (TEMPLATES .keys ())))
4880@click .option ("--input-file" , "-i" , type = click .Path (exists = True ), help = "Input text file" )
4981@click .option ("--text" , "-t" , help = "Input text directly" )
50- @click .option ("--output" , "-o" , type = click .Path (), help = "Output JSON file" )
82+ @click .option ("--output" , "-o" , type = click .Path (), help = "Output JSON file (default: auto-generated in data/)" )
83+ @click .option ("--data-dir" , default = "data" , help = "Directory for auto-generated outputs" )
5184@click .option ("--pretty" , is_flag = True , help = "Pretty print JSON output" )
85+ @click .option ("--no-save" , is_flag = True , help = "Don't save to file, only print to stdout" )
5286@click .pass_context
5387def extract (
5488 ctx : click .Context ,
5589 template : str ,
5690 input_file : Optional [str ],
5791 text : Optional [str ],
5892 output : Optional [str ],
59- pretty : bool
93+ data_dir : str ,
94+ pretty : bool ,
95+ no_save : bool
6096) -> None :
6197 """Extract data using a predefined template."""
6298 logger = ctx .obj ["logger" ]
@@ -82,20 +118,29 @@ def extract(
82118 click .echo (f"Extraction failed: { result .error } " , err = True )
83119 sys .exit (1 )
84120
121+ # Ensure we have data
122+ if result .data is None :
123+ click .echo ("Error: Extraction succeeded but no data returned" , err = True )
124+ sys .exit (1 )
125+
85126 # Format output
86127 indent = 2 if pretty else None
87128 output_json = json .dumps (result .data , indent = indent , ensure_ascii = False )
88129
89- # Write output
90- if output :
91- Path (output ).write_text (output_json , encoding = "utf-8" )
92- click .echo (f"Results saved to { output } " )
93- else :
130+ # Save to file unless --no-save is specified
131+ if not no_save :
132+ save_path = save_extraction_result (result .data , template , output , data_dir )
133+ click .echo (f"✅ Results saved to { save_path } " )
134+
135+ # Always print to stdout if no output file specified or if pretty print requested
136+ if not output or pretty or no_save :
137+ click .echo ("📄 Extraction Result:" )
94138 click .echo (output_json )
95139
96140 # Show stats
97141 if result .tokens_used :
98- logger .info (f"Tokens used: { result .tokens_used } " )
142+ click .echo (f"📊 Tokens used: { result .tokens_used } " )
143+ click .echo (f"💰 Estimated cost: ~${ (result .tokens_used * 0.00001 ):.4f} " ) # Rough estimate
99144
100145
101146@main .command ()
@@ -104,8 +149,10 @@ def extract(
104149@click .option ("--prompt" , help = "System prompt text" )
105150@click .option ("--input-file" , "-i" , type = click .Path (exists = True ), help = "Input text file" )
106151@click .option ("--text" , "-t" , help = "Input text directly" )
107- @click .option ("--output" , "-o" , type = click .Path (), help = "Output JSON file" )
152+ @click .option ("--output" , "-o" , type = click .Path (), help = "Output JSON file (default: auto-generated in data/)" )
153+ @click .option ("--data-dir" , default = "data" , help = "Directory for auto-generated outputs" )
108154@click .option ("--pretty" , is_flag = True , help = "Pretty print JSON output" )
155+ @click .option ("--no-save" , is_flag = True , help = "Don't save to file, only print to stdout" )
109156@click .pass_context
110157def extract_custom (
111158 ctx : click .Context ,
@@ -115,7 +162,9 @@ def extract_custom(
115162 input_file : Optional [str ],
116163 text : Optional [str ],
117164 output : Optional [str ],
118- pretty : bool
165+ data_dir : str ,
166+ pretty : bool ,
167+ no_save : bool
119168) -> None :
120169 """Extract data using a custom JSON schema."""
121170 logger = ctx .obj ["logger" ]
@@ -156,20 +205,29 @@ def extract_custom(
156205 click .echo (f"Extraction failed: { result .error } " , err = True )
157206 sys .exit (1 )
158207
208+ # Ensure we have data
209+ if result .data is None :
210+ click .echo ("Error: Extraction succeeded but no data returned" , err = True )
211+ sys .exit (1 )
212+
159213 # Format output
160214 indent = 2 if pretty else None
161215 output_json = json .dumps (result .data , indent = indent , ensure_ascii = False )
162216
163- # Write output
164- if output :
165- Path (output ).write_text (output_json , encoding = "utf-8" )
166- click .echo (f"Results saved to { output } " )
167- else :
217+ # Save to file unless --no-save is specified
218+ if not no_save :
219+ save_path = save_extraction_result (result .data , "custom" , output , data_dir )
220+ click .echo (f"✅ Results saved to { save_path } " )
221+
222+ # Always print to stdout if no output file specified or if pretty print requested
223+ if not output or pretty or no_save :
224+ click .echo ("📄 Extraction Result:" )
168225 click .echo (output_json )
169226
170227 # Show stats
171228 if result .tokens_used :
172- logger .info (f"Tokens used: { result .tokens_used } " )
229+ click .echo (f"📊 Tokens used: { result .tokens_used } " )
230+ click .echo (f"💰 Estimated cost: ~${ (result .tokens_used * 0.00001 ):.4f} " ) # Rough estimate
173231
174232
175233if __name__ == "__main__" :
0 commit comments