ifs-rag-assistant-cli 2.2.2__tar.gz → 2.3.0__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 (19) hide show
  1. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/PKG-INFO +5 -1
  2. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/README.md +4 -0
  3. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/__init__.py +1 -1
  4. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/backend_client.py +24 -0
  5. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/cli.py +102 -1
  6. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/PKG-INFO +5 -1
  7. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/pyproject.toml +1 -1
  8. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/auth_client.py +0 -0
  9. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/config.py +0 -0
  10. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/models.py +0 -0
  11. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/oauth.py +0 -0
  12. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/session.py +0 -0
  13. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/SOURCES.txt +0 -0
  14. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/dependency_links.txt +0 -0
  15. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/entry_points.txt +0 -0
  16. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/requires.txt +0 -0
  17. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/top_level.txt +0 -0
  18. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/setup.cfg +0 -0
  19. {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ifs-rag-assistant-cli
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: Command-line interface for IFS RAG Assistant Q&A system
5
5
  Home-page: https://github.com/maricaantonacci/ifs-rag-assistant-cli
6
6
  Author: Marica Antonacci
@@ -122,6 +122,8 @@ If you're using the ECMWF production environment, setup is simple:
122
122
 
123
123
  That's it! The configuration is already set up for ECMWF's production environment.
124
124
 
125
+ > **Network access disclaimer:** The CLI authentication flow is available only from approved ECMWF networks (CCI Cloud and VDI). From other networks, authentication will fail. For specific business needs, access exceptions can be evaluated with the ECMWF IAM team.
126
+
125
127
  ### For Custom Environments
126
128
 
127
129
  1. **Initialize and customize configuration**:
@@ -347,6 +349,8 @@ Logout and clear cached session:
347
349
  ifs-rag-assistant-cli logout
348
350
  ```
349
351
 
352
+ > **Network access disclaimer:** Authentication is available only from approved ECMWF networks (CCI Cloud and VDI). From other networks, authentication will fail. For specific business needs, access exceptions can be evaluated with the ECMWF IAM team.
353
+
350
354
  ### Initialize Configuration
351
355
 
352
356
  Create a configuration file pre-configured for ECMWF production:
@@ -93,6 +93,8 @@ If you're using the ECMWF production environment, setup is simple:
93
93
 
94
94
  That's it! The configuration is already set up for ECMWF's production environment.
95
95
 
96
+ > **Network access disclaimer:** The CLI authentication flow is available only from approved ECMWF networks (CCI Cloud and VDI). From other networks, authentication will fail. For specific business needs, access exceptions can be evaluated with the ECMWF IAM team.
97
+
96
98
  ### For Custom Environments
97
99
 
98
100
  1. **Initialize and customize configuration**:
@@ -318,6 +320,8 @@ Logout and clear cached session:
318
320
  ifs-rag-assistant-cli logout
319
321
  ```
320
322
 
323
+ > **Network access disclaimer:** Authentication is available only from approved ECMWF networks (CCI Cloud and VDI). From other networks, authentication will fail. For specific business needs, access exceptions can be evaluated with the ECMWF IAM team.
324
+
321
325
  ### Initialize Configuration
322
326
 
323
327
  Create a configuration file pre-configured for ECMWF production:
@@ -2,4 +2,4 @@
2
2
  IFS RAG Assistant CLI - Command-line interface for IFS RAG Assistant Q&A system.
3
3
  """
4
4
 
5
- __version__ = "2.2.2"
5
+ __version__ = "2.3.0"
@@ -173,6 +173,30 @@ class BackendClient:
173
173
 
174
174
  return cast(dict[str, Any], resp.json())
175
175
 
176
+ def get_chunk(self, chunk_id: str) -> dict[str, Any]:
177
+ """
178
+ Retrieve a single chunk by its identifier.
179
+
180
+ Args:
181
+ chunk_id: Chunk identifier from a citation
182
+
183
+ Returns:
184
+ Response dictionary with chunk metadata and text
185
+
186
+ Raises:
187
+ RuntimeError: If request fails
188
+ """
189
+ try:
190
+ resp = self.session.get(
191
+ f"{self.backend_url}/chunk/{chunk_id}",
192
+ timeout=self.timeout
193
+ )
194
+ resp.raise_for_status()
195
+ except RequestException as e:
196
+ self._raise_request_error("Get chunk request", e)
197
+
198
+ return cast(dict[str, Any], resp.json())
199
+
176
200
  def logout(self) -> None:
177
201
  """
178
202
  Logout from backend (invalidate session).
@@ -14,6 +14,7 @@ import typer
14
14
  from click.exceptions import Abort
15
15
  from rich.console import Console
16
16
  from rich.panel import Panel
17
+ from rich.syntax import Syntax
17
18
  from rich.table import Table
18
19
 
19
20
  from ifs_rag_assistant_cli import __version__
@@ -137,6 +138,103 @@ def format_citations(citations_data: list, plain: bool = False) -> None:
137
138
  console.print(table)
138
139
 
139
140
 
141
+ def guess_chunk_language(path: str, kind: str = "") -> str:
142
+ """Guess syntax lexer from file path and symbol kind."""
143
+ path_lower = str(path).lower()
144
+ if path_lower.endswith((".f90", ".f", ".for", ".f77", ".f95", ".f03", ".f08")):
145
+ return "fortran"
146
+ if path_lower.endswith((".md", ".markdown")):
147
+ return "markdown"
148
+ if path_lower.endswith((".py",)):
149
+ return "python"
150
+ if path_lower.endswith((".yml", ".yaml")):
151
+ return "yaml"
152
+ if "markdown" in str(kind).lower():
153
+ return "markdown"
154
+ return "text"
155
+
156
+
157
+ def display_chunk(chunk_data: dict, plain: bool = False) -> None:
158
+ """Render chunk content and metadata in terminal."""
159
+ path = str(chunk_data.get("path", ""))
160
+ start = int(chunk_data.get("start", 1) or 1)
161
+ end = int(chunk_data.get("end", start) or start)
162
+ kind = str(chunk_data.get("kind", ""))
163
+ sym_name = str(chunk_data.get("sym_name", ""))
164
+ text = str(chunk_data.get("text", ""))
165
+
166
+ if plain:
167
+ console.print(f"\nChunk: {path}:{start}-{end} ({kind or 'n/a'})")
168
+ if sym_name:
169
+ console.print(f"Symbol: {sym_name}")
170
+ console.print(text if text else "<empty chunk>")
171
+ return
172
+
173
+ title = f"Chunk: {path}:{start}-{end}"
174
+ subtitle = f"{kind or 'n/a'}"
175
+ if sym_name:
176
+ subtitle = f"{subtitle} - {sym_name}"
177
+
178
+ syntax = Syntax(
179
+ text if text else "<empty chunk>",
180
+ guess_chunk_language(path, kind),
181
+ line_numbers=True,
182
+ start_line=start,
183
+ word_wrap=False,
184
+ )
185
+ console.print(Panel(syntax, title=title, subtitle=subtitle, border_style="cyan"))
186
+
187
+
188
+ def inspect_citations_interactively(client, citations_data: list, plain: bool = False) -> None:
189
+ """Allow users to fetch and inspect full chunk content from citations."""
190
+ indexed_citations: list[tuple[int, Citation]] = []
191
+ for i, cite_dict in enumerate(citations_data, 1):
192
+ citation = Citation.from_dict(cite_dict)
193
+ indexed_citations.append((i, citation))
194
+
195
+ inspectable = [(i, c) for i, c in indexed_citations if c.id]
196
+ if not inspectable:
197
+ return
198
+
199
+ try:
200
+ if not typer.confirm("\n🔎 Inspect a citation chunk?", default=False):
201
+ return
202
+
203
+ valid_numbers = {i for i, _ in inspectable}
204
+ choices_text = ", ".join(str(i) for i, _ in inspectable)
205
+
206
+ while True:
207
+ selected = typer.prompt(
208
+ f"Select citation number ({choices_text}) or 0 to cancel",
209
+ type=int,
210
+ default=0,
211
+ )
212
+
213
+ if selected == 0:
214
+ return
215
+
216
+ if selected not in valid_numbers:
217
+ console.print("[yellow]Invalid citation number[/yellow]")
218
+ continue
219
+
220
+ citation = next(c for i, c in inspectable if i == selected)
221
+ try:
222
+ chunk_data = client.backend.get_chunk(str(citation.id))
223
+ display_chunk(chunk_data, plain=plain)
224
+ except SessionExpiredError as e:
225
+ console.print(f"[bold red]❌ Error:[/bold red] {e}")
226
+ return
227
+ except RuntimeError as e:
228
+ console.print(f"[yellow]⚠️ Failed to fetch chunk: {e}[/yellow]")
229
+ return
230
+
231
+ if not typer.confirm("Inspect another citation?", default=False):
232
+ return
233
+ except (Abort, KeyboardInterrupt):
234
+ console.print("\n[dim]Citation inspection cancelled[/dim]")
235
+ return
236
+
237
+
140
238
  def show_auth_mode_notice(config) -> None:
141
239
  """Show an explicit warning when authentication is disabled."""
142
240
  auth_mode = str(getattr(config, "auth_mode", "oauth")).strip().lower()
@@ -230,6 +328,7 @@ def chat(
230
328
  # Show citations if available
231
329
  citations_data = result.get("citations", [])
232
330
  format_citations(citations_data, plain)
331
+ inspect_citations_interactively(client, citations_data, plain=plain)
233
332
 
234
333
  # Ask for optional feedback
235
334
  if "conversation_id" in result and "answer_id" in result:
@@ -307,6 +406,7 @@ def ask(
307
406
  if citations_data:
308
407
  console.print("\n")
309
408
  format_citations(citations_data, plain)
409
+ inspect_citations_interactively(client, citations_data, plain=plain)
310
410
 
311
411
  # Ask for optional feedback
312
412
  if "conversation_id" in result and "answer_id" in result:
@@ -340,7 +440,7 @@ def search(
340
440
  None,
341
441
  "--config",
342
442
  help="Path to config file"
343
- )
443
+ ),
344
444
  ) -> None:
345
445
  """
346
446
  Search the RAG index.
@@ -364,6 +464,7 @@ def search(
364
464
  citations_data = result.get("citations", [])
365
465
  if citations_data:
366
466
  format_citations(citations_data, plain=False)
467
+ inspect_citations_interactively(client, citations_data, plain=False)
367
468
  else:
368
469
  console.print("[yellow]No results found[/yellow]")
369
470
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ifs-rag-assistant-cli
3
- Version: 2.2.2
3
+ Version: 2.3.0
4
4
  Summary: Command-line interface for IFS RAG Assistant Q&A system
5
5
  Home-page: https://github.com/maricaantonacci/ifs-rag-assistant-cli
6
6
  Author: Marica Antonacci
@@ -122,6 +122,8 @@ If you're using the ECMWF production environment, setup is simple:
122
122
 
123
123
  That's it! The configuration is already set up for ECMWF's production environment.
124
124
 
125
+ > **Network access disclaimer:** The CLI authentication flow is available only from approved ECMWF networks (CCI Cloud and VDI). From other networks, authentication will fail. For specific business needs, access exceptions can be evaluated with the ECMWF IAM team.
126
+
125
127
  ### For Custom Environments
126
128
 
127
129
  1. **Initialize and customize configuration**:
@@ -347,6 +349,8 @@ Logout and clear cached session:
347
349
  ifs-rag-assistant-cli logout
348
350
  ```
349
351
 
352
+ > **Network access disclaimer:** Authentication is available only from approved ECMWF networks (CCI Cloud and VDI). From other networks, authentication will fail. For specific business needs, access exceptions can be evaluated with the ECMWF IAM team.
353
+
350
354
  ### Initialize Configuration
351
355
 
352
356
  Create a configuration file pre-configured for ECMWF production:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ifs-rag-assistant-cli"
7
- version = "2.2.2"
7
+ version = "2.3.0"
8
8
  description = "Command-line interface for IFS RAG Assistant Q&A system"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"