vectorgov-cli 0.3.2__tar.gz → 0.3.4__tar.gz

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 (30) hide show
  1. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/PKG-INFO +2 -2
  2. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/pyproject.toml +2 -2
  3. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/__init__.py +1 -1
  4. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/fs_search.py +4 -0
  5. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/grep_cmd.py +4 -0
  6. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/hybrid.py +47 -0
  7. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/lookup.py +3 -0
  8. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/merged.py +4 -0
  9. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/smart_search.py +4 -0
  10. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/utils/output.py +60 -0
  11. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/.gitignore +0 -0
  12. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/README.md +0 -0
  13. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/__init__.py +0 -0
  14. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/ask.py +0 -0
  15. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/audit.py +0 -0
  16. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/auth.py +0 -0
  17. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/config.py +0 -0
  18. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/context.py +0 -0
  19. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/docs.py +0 -0
  20. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/explain.py +0 -0
  21. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/feedback.py +0 -0
  22. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/init.py +0 -0
  23. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/prompts.py +0 -0
  24. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/quota.py +0 -0
  25. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/read.py +0 -0
  26. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/search.py +0 -0
  27. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/commands/tokens.py +0 -0
  28. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/main.py +0 -0
  29. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/utils/__init__.py +0 -0
  30. {vectorgov_cli-0.3.2 → vectorgov_cli-0.3.4}/src/vectorgov/cli/utils/config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vectorgov-cli
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: CLI para a API VectorGov - Busca semântica em legislação brasileira
5
5
  Project-URL: Homepage, https://vectorgov.io
6
6
  Project-URL: Documentation, https://vectorgov.io/documentacao
@@ -25,7 +25,7 @@ Requires-Python: >=3.9
25
25
  Requires-Dist: pyyaml>=6.0.0
26
26
  Requires-Dist: rich>=13.0.0
27
27
  Requires-Dist: typer>=0.9.0
28
- Requires-Dist: vectorgov>=0.13.0
28
+ Requires-Dist: vectorgov>=0.19.1
29
29
  Provides-Extra: all
30
30
  Requires-Dist: anthropic>=0.18.0; extra == 'all'
31
31
  Requires-Dist: google-generativeai>=0.3.0; extra == 'all'
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "vectorgov-cli"
7
- version = "0.3.2"
7
+ version = "0.3.4"
8
8
  description = "CLI para a API VectorGov - Busca semântica em legislação brasileira"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -37,7 +37,7 @@ classifiers = [
37
37
  "Topic :: Text Processing :: Linguistic",
38
38
  ]
39
39
  dependencies = [
40
- "vectorgov>=0.13.0",
40
+ "vectorgov>=0.19.1",
41
41
  "typer>=0.9.0",
42
42
  "rich>=13.0.0",
43
43
  "pyyaml>=6.0.0",
@@ -7,7 +7,7 @@ Uso:
7
7
  vectorgov auth login
8
8
  """
9
9
 
10
- __version__ = "0.3.2"
10
+ __version__ = "0.3.4"
11
11
 
12
12
  from .main import app
13
13
 
@@ -6,8 +6,10 @@ from rich.console import Console
6
6
  from rich.table import Table
7
7
  from ..utils.config import ConfigManager
8
8
  from ..utils.output import (
9
+ extract_credits,
9
10
  extract_request_id,
10
11
  get_evidence_links,
12
+ print_credits_footer,
11
13
  print_request_id_footer,
12
14
  render_evidence_lines_text,
13
15
  render_llm_output,
@@ -100,6 +102,7 @@ def fs_search(
100
102
  "latency_ms": getattr(result, "latency_ms", None),
101
103
  "query_id": getattr(result, "query_id", None),
102
104
  "request_id": extract_request_id(result),
105
+ "credits": extract_credits(result),
103
106
  }
104
107
  print(render_llm_output(hits, query, metadata))
105
108
  return
@@ -153,6 +156,7 @@ def fs_search(
153
156
  if query_id:
154
157
  console.print(f"\n[dim]query_id: {query_id}[/dim]")
155
158
  print_request_id_footer(console, extract_request_id(result))
159
+ print_credits_footer(console, result)
156
160
 
157
161
  except typer.Exit:
158
162
  raise
@@ -6,8 +6,10 @@ from rich.console import Console
6
6
  from rich.table import Table
7
7
  from ..utils.config import ConfigManager
8
8
  from ..utils.output import (
9
+ extract_credits,
9
10
  extract_request_id,
10
11
  get_evidence_links,
12
+ print_credits_footer,
11
13
  print_request_id_footer,
12
14
  render_evidence_lines_text,
13
15
  render_llm_output,
@@ -92,6 +94,7 @@ def grep(
92
94
  "latency_ms": getattr(result, "latency_ms", None),
93
95
  "query_id": None,
94
96
  "request_id": extract_request_id(result),
97
+ "credits": extract_credits(result),
95
98
  }
96
99
  print(render_llm_output(hits, query, metadata))
97
100
  return
@@ -132,6 +135,7 @@ def grep(
132
135
  console.print()
133
136
 
134
137
  print_request_id_footer(console, extract_request_id(result))
138
+ print_credits_footer(console, result)
135
139
 
136
140
  except Exception as e:
137
141
  console.print(f"[red]Erro:[/red] {e}")
@@ -6,8 +6,10 @@ from rich.console import Console
6
6
  from rich.table import Table
7
7
  from ..utils.config import ConfigManager
8
8
  from ..utils.output import (
9
+ extract_credits,
9
10
  extract_request_id,
10
11
  get_evidence_links,
12
+ print_credits_footer,
11
13
  print_request_id_footer,
12
14
  render_evidence_lines_text,
13
15
  render_llm_output,
@@ -27,6 +29,10 @@ def hybrid(
27
29
  help="Tipo de expansão: bidirectional ou forward",
28
30
  ),
29
31
  token_budget: int = typer.Option(None, "--token-budget", help="Limite de tokens para contexto"),
32
+ show_stats: bool = typer.Option(
33
+ False, "--show-stats",
34
+ help="Mostra estatísticas de expansão (seeds, graph_nodes, tokens) no rodapé"
35
+ ),
30
36
  output: str = typer.Option("table", "--output", "-o", help="Formato: table, json, text, llm"),
31
37
  raw: bool = typer.Option(False, "--raw", help="JSON bruto para piping"),
32
38
  ):
@@ -38,6 +44,7 @@ def hybrid(
38
44
  Exemplo:
39
45
  vectorgov hybrid "Critérios de julgamento em licitações"
40
46
  vectorgov hybrid "Dispensa de licitação" --hops 2 --top-k 15
47
+ vectorgov hybrid "ETP" --show-stats # inclui estatisticas
41
48
  """
42
49
  try:
43
50
  from vectorgov import VectorGov
@@ -132,6 +139,7 @@ def hybrid(
132
139
  "latency_ms": getattr(result, "latency_ms", None),
133
140
  "query_id": getattr(result, "query_id", None),
134
141
  "request_id": extract_request_id(result),
142
+ "credits": extract_credits(result),
135
143
  }
136
144
  print(render_llm_output(hits, query, metadata))
137
145
  return
@@ -176,10 +184,49 @@ def hybrid(
176
184
  for line in render_evidence_lines_text(h):
177
185
  console.print(f"[dim]{line}[/dim]")
178
186
 
187
+ # Estatisticas de expansao (--show-stats): seeds, graph nodes e
188
+ # tokens do contexto LLM. Util para entender o quanto o grafo
189
+ # contribuiu vs busca direta, sem precisar de --raw.
190
+ if show_stats:
191
+ stats_obj = getattr(result, "stats", None)
192
+ seeds = None
193
+ graph_nodes_count = None
194
+ total_tokens = None
195
+ truncated = None
196
+ if stats_obj is not None:
197
+ # stats pode ser dict (SDK antigo) ou objeto com atributos
198
+ if isinstance(stats_obj, dict):
199
+ seeds = stats_obj.get("seeds") or stats_obj.get("seeds_count")
200
+ graph_nodes_count = stats_obj.get("graph_nodes") or stats_obj.get("nodes")
201
+ total_tokens = stats_obj.get("tokens") or stats_obj.get("total_tokens")
202
+ truncated = stats_obj.get("truncated")
203
+ else:
204
+ seeds = getattr(stats_obj, "seeds", None) or getattr(stats_obj, "seeds_count", None)
205
+ graph_nodes_count = getattr(stats_obj, "graph_nodes", None) or getattr(stats_obj, "nodes", None)
206
+ total_tokens = getattr(stats_obj, "tokens", None) or getattr(stats_obj, "total_tokens", None)
207
+ truncated = getattr(stats_obj, "truncated", None)
208
+ # Fallback: contar diretamente dos hits se stats nao veio
209
+ if seeds is None:
210
+ seeds = sum(1 for h in direct_hits)
211
+ if graph_nodes_count is None:
212
+ graph_nodes_count = sum(1 for h in graph_hits)
213
+
214
+ console.print()
215
+ stats_line = (
216
+ f"[dim]Stats: seeds={seeds} | graph_nodes={graph_nodes_count}"
217
+ )
218
+ if total_tokens is not None:
219
+ stats_line += f" | tokens={total_tokens}"
220
+ if truncated is not None:
221
+ stats_line += f" | truncated={'sim' if truncated else 'nao'}"
222
+ stats_line += "[/dim]"
223
+ console.print(stats_line)
224
+
179
225
  query_id = getattr(result, "query_id", None)
180
226
  if query_id:
181
227
  console.print(f"\n[dim]query_id: {query_id}[/dim]")
182
228
  print_request_id_footer(console, extract_request_id(result))
229
+ print_credits_footer(console, result)
183
230
 
184
231
  except Exception as e:
185
232
  console.print(f"[red]Erro:[/red] {e}")
@@ -7,7 +7,9 @@ from rich.panel import Panel
7
7
  from ..utils.config import ConfigManager
8
8
  from ..utils.output import (
9
9
  absolute_url,
10
+ extract_credits,
10
11
  extract_request_id,
12
+ print_credits_footer,
11
13
  print_request_id_footer,
12
14
  render_evidence_lines_text,
13
15
  render_llm_output,
@@ -425,6 +427,7 @@ def lookup( # noqa: C901
425
427
  "latency_ms": getattr(result, "latency_ms", None),
426
428
  "query_id": getattr(result, "query_id", None),
427
429
  "request_id": extract_request_id(result, raw_resp),
430
+ "credits": extract_credits(result),
428
431
  "nota_especialista": nota_especialista,
429
432
  "jurisprudencia_tcu": jurisprudencia_tcu,
430
433
  "acordao_tcu_key": acordao_tcu_key,
@@ -6,8 +6,10 @@ from rich.console import Console
6
6
  from rich.table import Table
7
7
  from ..utils.config import ConfigManager
8
8
  from ..utils.output import (
9
+ extract_credits,
9
10
  extract_request_id,
10
11
  get_evidence_links,
12
+ print_credits_footer,
11
13
  print_request_id_footer,
12
14
  render_evidence_lines_text,
13
15
  render_llm_output,
@@ -132,6 +134,7 @@ def merged(
132
134
  "latency_ms": getattr(result, "latency_ms", None),
133
135
  "query_id": None,
134
136
  "request_id": extract_request_id(result),
137
+ "credits": extract_credits(result),
135
138
  }
136
139
  print(render_llm_output(hits, query, metadata))
137
140
  return
@@ -167,6 +170,7 @@ def merged(
167
170
  console.print(f"[dim]{line}[/dim]")
168
171
 
169
172
  print_request_id_footer(console, extract_request_id(result))
173
+ print_credits_footer(console, result)
170
174
 
171
175
  except typer.Exit:
172
176
  # typer.Exit(0) usado para "nenhum resultado" nao deve ser tratado
@@ -7,8 +7,10 @@ from rich.table import Table
7
7
  from rich.panel import Panel
8
8
  from ..utils.config import ConfigManager
9
9
  from ..utils.output import (
10
+ extract_credits,
10
11
  extract_request_id,
11
12
  get_evidence_links,
13
+ print_credits_footer,
12
14
  print_request_id_footer,
13
15
  render_evidence_lines_text,
14
16
  render_llm_output,
@@ -97,6 +99,7 @@ def smart_search(
97
99
  "latency_ms": getattr(result, "latency_ms", None),
98
100
  "query_id": getattr(result, "query_id", None),
99
101
  "request_id": extract_request_id(result),
102
+ "credits": extract_credits(result),
100
103
  }
101
104
  print(render_llm_output(hits, query, metadata))
102
105
  return
@@ -148,6 +151,7 @@ def smart_search(
148
151
  f"(use com 'vectorgov feedback send')[/dim]"
149
152
  )
150
153
  print_request_id_footer(console, extract_request_id(result))
154
+ print_credits_footer(console, result)
151
155
 
152
156
  except Exception as e:
153
157
  console.print(f"[red]Erro:[/red] {e}")
@@ -175,6 +175,56 @@ def extract_request_id(result: Any, raw_resp: Optional[Dict[str, Any]] = None) -
175
175
  return None
176
176
 
177
177
 
178
+ def extract_credits(result: Any) -> Optional[Dict[str, Any]]:
179
+ """Extrai CreditsInfo (cost, balance, endpoint) do result do SDK.
180
+
181
+ Requer vectorgov SDK >= 0.19.1 (expoe result.credits). Retorna None
182
+ se o SDK nao populou (versao antiga ou endpoint sem custo).
183
+
184
+ Returns:
185
+ Dict com keys 'cost' (int), 'balance' (int), 'endpoint' (str)
186
+ ou None se ausente.
187
+ """
188
+ credits = getattr(result, "credits", None)
189
+ if credits is None:
190
+ return None
191
+ return {
192
+ "cost": getattr(credits, "cost", 0),
193
+ "balance": getattr(credits, "balance", 0),
194
+ "endpoint": getattr(credits, "endpoint", ""),
195
+ }
196
+
197
+
198
+ def format_credits_line(result: Any, prefix: str = "") -> Optional[str]:
199
+ """Formata linha de creditos para o rodape text/llm.
200
+
201
+ Retorna None se o SDK nao expos credits (nao renderiza nada).
202
+
203
+ Exemplo de saida:
204
+ 'Creditos: 1 (saldo: 68)'
205
+ """
206
+ credits = extract_credits(result)
207
+ if not credits:
208
+ return None
209
+ cost = credits.get("cost", 0)
210
+ balance = credits.get("balance", 0)
211
+ return f"{prefix}Créditos: {cost} (saldo: {balance})"
212
+
213
+
214
+ def print_credits_footer(console: Console, result: Any) -> None:
215
+ """Imprime rodape formatado 'Creditos: X (saldo: Y)' se disponivel.
216
+
217
+ Use no final dos handlers de output text dos comandos de busca,
218
+ para exibir custo da chamada e saldo restante. Dim style para
219
+ nao competir com o conteudo principal.
220
+
221
+ Silencioso se SDK < 0.19.1 (result.credits e None).
222
+ """
223
+ line = format_credits_line(result)
224
+ if line:
225
+ console.print(f"[dim]{line}[/dim]")
226
+
227
+
178
228
  def render_evidence_cell_table(hit: Any) -> str:
179
229
  """Célula curta para coluna de tabela. Usa Rich link markup clicável."""
180
230
  links = get_evidence_links(hit)
@@ -271,6 +321,11 @@ def render_llm_output(hits: list, query: str, metadata: Optional[dict] = None) -
271
321
  parts.append(f"Total: {metadata['total']} hits")
272
322
  if metadata.get("latency_ms") is not None:
273
323
  parts.append(f"Latencia: {metadata['latency_ms']/1000:.1f}s")
324
+ credits_info = metadata.get("credits")
325
+ if credits_info:
326
+ cost = credits_info.get("cost", 0)
327
+ balance = credits_info.get("balance", 0)
328
+ parts.append(f"Creditos: {cost} (saldo: {balance})")
274
329
  if metadata.get("query_id"):
275
330
  parts.append(f"Query ID: {metadata['query_id']}")
276
331
  if metadata.get("request_id"):
@@ -345,6 +400,7 @@ def format_search_results(console: Console, results: Any, output_format: OutputF
345
400
  query: Query original
346
401
  """
347
402
  request_id = extract_request_id(results)
403
+ credits = extract_credits(results)
348
404
 
349
405
  if output_format == OutputFormat.json:
350
406
  data = {
@@ -354,6 +410,7 @@ def format_search_results(console: Console, results: Any, output_format: OutputF
354
410
  "latency_ms": results.latency_ms,
355
411
  "query_id": results.query_id,
356
412
  "request_id": request_id,
413
+ "credits": credits,
357
414
  "hits": [hit_to_raw_dict(h) for h in results.hits],
358
415
  }
359
416
  console.print_json(json.dumps(data, ensure_ascii=False))
@@ -407,6 +464,7 @@ def format_search_results(console: Console, results: Any, output_format: OutputF
407
464
  console.print(f"[dim]Query ID: {results.query_id}[/dim]")
408
465
  if request_id:
409
466
  console.print(f"[dim]Request ID: {request_id}[/dim]")
467
+ print_credits_footer(console, results)
410
468
  console.print("[dim]Use 'vectorgov feedback send <query_id> --like' para avaliar[/dim]")
411
469
 
412
470
  elif output_format == OutputFormat.markdown:
@@ -432,6 +490,7 @@ def format_search_results(console: Console, results: Any, output_format: OutputF
432
490
  "latency_ms": results.latency_ms,
433
491
  "query_id": results.query_id,
434
492
  "request_id": request_id,
493
+ "credits": credits,
435
494
  }
436
495
  print(render_llm_output(results.hits, query, metadata))
437
496
 
@@ -462,6 +521,7 @@ def format_search_results(console: Console, results: Any, output_format: OutputF
462
521
  console.print(f"[dim]Query ID: {results.query_id}[/dim]")
463
522
  if request_id:
464
523
  console.print(f"[dim]Request ID: {request_id}[/dim]")
524
+ print_credits_footer(console, results)
465
525
 
466
526
 
467
527
  def format_code_block(console: Console, code: str, language: str = "python"):
File without changes
File without changes