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.
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/PKG-INFO +5 -1
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/README.md +4 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/__init__.py +1 -1
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/backend_client.py +24 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/cli.py +102 -1
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/PKG-INFO +5 -1
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/pyproject.toml +1 -1
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/auth_client.py +0 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/config.py +0 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/models.py +0 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/oauth.py +0 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/session.py +0 -0
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/SOURCES.txt +0 -0
- {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
- {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
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/requires.txt +0 -0
- {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
- {ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/setup.cfg +0 -0
- {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.
|
|
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:
|
{ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/backend_client.py
RENAMED
|
@@ -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
|
|
{ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ifs-rag-assistant-cli
|
|
3
|
-
Version: 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:
|
{ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/auth_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ifs_rag_assistant_cli-2.2.2 → ifs_rag_assistant_cli-2.3.0}/ifs_rag_assistant_cli/session.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|