pointblank 0.14.0__py3-none-any.whl → 0.16.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.
@@ -0,0 +1,660 @@
1
+ import inspect
2
+ import re
3
+ from pathlib import Path
4
+ from typing import Optional
5
+ from urllib.parse import urljoin
6
+
7
+ try:
8
+ import requests
9
+
10
+ SCRAPING_AVAILABLE = True
11
+ except ImportError:
12
+ SCRAPING_AVAILABLE = False
13
+
14
+
15
+ def get_api_details(module, exported_list):
16
+ """
17
+ Retrieve the signatures and docstrings of the functions/classes in the exported list.
18
+
19
+ Parameters
20
+ ----------
21
+ module : module
22
+ The module from which to retrieve the functions/classes.
23
+ exported_list : list
24
+ A list of function/class names as strings.
25
+
26
+ Returns
27
+ -------
28
+ str
29
+ A string containing the combined class name, signature, and docstring.
30
+ """
31
+ api_text = ""
32
+
33
+ for fn in exported_list:
34
+ # Split the attribute path to handle nested attributes
35
+ parts = fn.split(".")
36
+ obj = module
37
+ for part in parts:
38
+ obj = getattr(obj, part)
39
+
40
+ # Get the name of the object
41
+ obj_name = obj.__name__
42
+
43
+ # Get the function signature
44
+ sig = inspect.signature(obj)
45
+
46
+ # Get the docstring
47
+ doc = obj.__doc__
48
+
49
+ # Combine the class name, signature, and docstring
50
+ api_text += f"{obj_name}{sig}\n{doc}\n\n"
51
+
52
+ return api_text
53
+
54
+
55
+ def _get_api_text() -> str:
56
+ """
57
+ Get the API documentation for the Pointblank library.
58
+
59
+ Returns
60
+ -------
61
+ str
62
+ The API documentation for the Pointblank library.
63
+ """
64
+
65
+ import pointblank
66
+
67
+ sep_line = "-" * 70
68
+
69
+ api_text = (
70
+ f"{sep_line}\nThis is the API documentation for the Pointblank library.\n{sep_line}\n\n"
71
+ )
72
+
73
+ #
74
+ # Lists of exported functions and methods in different families
75
+ #
76
+
77
+ validate_exported = [
78
+ "Validate",
79
+ "Thresholds",
80
+ "Actions",
81
+ "FinalActions",
82
+ "Schema",
83
+ "DraftValidation",
84
+ ]
85
+
86
+ val_steps_exported = [
87
+ "Validate.col_vals_gt",
88
+ "Validate.col_vals_lt",
89
+ "Validate.col_vals_ge",
90
+ "Validate.col_vals_le",
91
+ "Validate.col_vals_eq",
92
+ "Validate.col_vals_ne",
93
+ "Validate.col_vals_between",
94
+ "Validate.col_vals_outside",
95
+ "Validate.col_vals_in_set",
96
+ "Validate.col_vals_not_in_set",
97
+ "Validate.col_vals_increasing",
98
+ "Validate.col_vals_decreasing",
99
+ "Validate.col_vals_null",
100
+ "Validate.col_vals_not_null",
101
+ "Validate.col_vals_regex",
102
+ "Validate.col_vals_within_spec",
103
+ "Validate.col_vals_expr",
104
+ "Validate.rows_distinct",
105
+ "Validate.rows_complete",
106
+ "Validate.col_exists",
107
+ "Validate.col_schema_match",
108
+ "Validate.row_count_match",
109
+ "Validate.col_count_match",
110
+ "Validate.tbl_match",
111
+ "Validate.conjointly",
112
+ "Validate.specially",
113
+ "Validate.prompt",
114
+ ]
115
+
116
+ column_selection_exported = [
117
+ "col",
118
+ "starts_with",
119
+ "ends_with",
120
+ "contains",
121
+ "matches",
122
+ "everything",
123
+ "first_n",
124
+ "last_n",
125
+ "expr_col",
126
+ ]
127
+
128
+ segments_exported = [
129
+ "seg_group",
130
+ ]
131
+
132
+ interrogation_exported = [
133
+ "Validate.interrogate",
134
+ "Validate.set_tbl",
135
+ "Validate.get_tabular_report",
136
+ "Validate.get_step_report",
137
+ "Validate.get_json_report",
138
+ "Validate.get_sundered_data",
139
+ "Validate.get_data_extracts",
140
+ "Validate.all_passed",
141
+ "Validate.assert_passing",
142
+ "Validate.assert_below_threshold",
143
+ "Validate.above_threshold",
144
+ "Validate.n",
145
+ "Validate.n_passed",
146
+ "Validate.n_failed",
147
+ "Validate.f_passed",
148
+ "Validate.f_failed",
149
+ "Validate.warning",
150
+ "Validate.error",
151
+ "Validate.critical",
152
+ ]
153
+
154
+ inspect_exported = [
155
+ "DataScan",
156
+ "preview",
157
+ "col_summary_tbl",
158
+ "missing_vals_tbl",
159
+ "assistant",
160
+ "load_dataset",
161
+ "get_data_path",
162
+ "connect_to_table",
163
+ "print_database_tables",
164
+ ]
165
+
166
+ yaml_exported = [
167
+ "yaml_interrogate",
168
+ "validate_yaml",
169
+ "yaml_to_python",
170
+ ]
171
+
172
+ utility_exported = [
173
+ "get_column_count",
174
+ "get_row_count",
175
+ "get_action_metadata",
176
+ "get_validation_summary",
177
+ "write_file",
178
+ "read_file",
179
+ "config",
180
+ ]
181
+
182
+ prebuilt_actions_exported = [
183
+ "send_slack_notification",
184
+ ]
185
+
186
+ validate_desc = """When peforming data validation, you'll need the `Validate` class to get the
187
+ process started. It's given the target table and you can optionally provide some metadata and/or
188
+ failure thresholds (using the `Thresholds` class or through shorthands for this task). The
189
+ `Validate` class has numerous methods for defining validation steps and for obtaining
190
+ post-interrogation metrics and data."""
191
+
192
+ val_steps_desc = """Validation steps can be thought of as sequential validations on the target
193
+ data. We call `Validate`'s validation methods to build up a validation plan: a collection of steps
194
+ that, in the aggregate, provides good validation coverage."""
195
+
196
+ column_selection_desc = """A flexible way to select columns for validation is to use the `col()`
197
+ function along with column selection helper functions. A combination of `col()` + `starts_with()`,
198
+ `matches()`, etc., allows for the selection of multiple target columns (mapping a validation across
199
+ many steps). Furthermore, the `col()` function can be used to declare a comparison column (e.g.,
200
+ for the `value=` argument in many `col_vals_*()` methods) when you can't use a fixed value
201
+ for comparison."""
202
+
203
+ segments_desc = (
204
+ """Combine multiple values into a single segment using `seg_*()` helper functions."""
205
+ )
206
+
207
+ interrogation_desc = """The validation plan is put into action when `interrogate()` is called.
208
+ The workflow for performing a comprehensive validation is then: (1) `Validate()`, (2) adding
209
+ validation steps, (3) `interrogate()`. After interrogation of the data, we can view a validation
210
+ report table (by printing the object or using `get_tabular_report()`), extract key metrics, or we
211
+ can split the data based on the validation results (with `get_sundered_data()`)."""
212
+
213
+ inspect_desc = """The *Inspection and Assistance* group contains functions that are helpful for
214
+ getting to grips on a new data table. Use the `DataScan` class to get a quick overview of the data,
215
+ `preview()` to see the first and last few rows of a table, `col_summary_tbl()` for a column-level
216
+ summary of a table, `missing_vals_tbl()` to see where there are missing values in a table, and
217
+ `get_column_count()`/`get_row_count()` to get the number of columns and rows in a table. Several
218
+ datasets included in the package can be accessed via the `load_dataset()` function. Finally, the
219
+ `config()` utility lets us set global configuration parameters. Want to chat with an assistant? Use
220
+ the `assistant()` function to get help with Pointblank."""
221
+
222
+ yaml_desc = """The *YAML* group contains functions that allow for the use of YAML to orchestrate
223
+ validation workflows. The `yaml_interrogate()` function can be used to run a validation workflow
224
+ from YAML strings or files. The `validate_yaml()` function checks if the YAML configuration passes
225
+ its own validity checks. The `yaml_to_python()` function converts YAML configuration to equivalent
226
+ Python code."""
227
+
228
+ utility_desc = """The Utility Functions group contains functions that are useful for accessing
229
+ metadata about the target data. Use `get_column_count()` or `get_row_count()` to get the number of
230
+ columns or rows in a table. The `get_action_metadata()` function is useful when building custom
231
+ actions since it returns metadata about the validation step that's triggering the action. Lastly,
232
+ the `config()` utility lets us set global configuration parameters."""
233
+
234
+ prebuilt_actions_desc = """The Prebuilt Actions group contains a function that can be used to
235
+ send a Slack notification when validation steps exceed failure threshold levels or just to provide a
236
+ summary of the validation results, including the status, number of steps, passing and failing steps,
237
+ table information, and timing details."""
238
+
239
+ #
240
+ # Add headings (`*_desc` text) and API details for each family of functions/methods
241
+ #
242
+
243
+ api_text += f"""\n## The Validate family\n\n{validate_desc}\n\n"""
244
+ api_text += get_api_details(module=pointblank, exported_list=validate_exported)
245
+
246
+ api_text += f"""\n## The Validation Steps family\n\n{val_steps_desc}\n\n"""
247
+ api_text += get_api_details(module=pointblank, exported_list=val_steps_exported)
248
+
249
+ api_text += f"""\n## The Column Selection family\n\n{column_selection_desc}\n\n"""
250
+ api_text += get_api_details(module=pointblank, exported_list=column_selection_exported)
251
+
252
+ api_text += f"""\n## The Segments family\n\n{segments_desc}\n\n"""
253
+ api_text += get_api_details(module=pointblank, exported_list=segments_exported)
254
+
255
+ api_text += f"""\n## The Interrogation and Reporting family\n\n{interrogation_desc}\n\n"""
256
+ api_text += get_api_details(module=pointblank, exported_list=interrogation_exported)
257
+
258
+ api_text += f"""\n## The Inspection and Assistance family\n\n{inspect_desc}\n\n"""
259
+ api_text += get_api_details(module=pointblank, exported_list=inspect_exported)
260
+
261
+ api_text += f"""\n## The YAML family\n\n{yaml_desc}\n\n"""
262
+ api_text += get_api_details(module=pointblank, exported_list=yaml_exported)
263
+
264
+ api_text += f"""\n## The Utility Functions family\n\n{utility_desc}\n\n"""
265
+ api_text += get_api_details(module=pointblank, exported_list=utility_exported)
266
+
267
+ api_text += f"""\n## The Prebuilt Actions family\n\n{prebuilt_actions_desc}\n\n"""
268
+ api_text += get_api_details(module=pointblank, exported_list=prebuilt_actions_exported)
269
+
270
+ # Modify language syntax in all code cells
271
+ api_text = api_text.replace("{python}", "python")
272
+
273
+ # Remove code cells that contain `#| echo: false` (i.e., don't display the code)
274
+ api_text = re.sub(r"```python\n\s*.*\n\s*.*\n.*\n.*\n.*```\n\s*", "", api_text)
275
+
276
+ return api_text
277
+
278
+
279
+ def _get_examples_text() -> str:
280
+ """
281
+ Get the examples for the Pointblank library. These examples are extracted from the Quarto
282
+ documents in the `docs/demos` directory.
283
+
284
+ Returns
285
+ -------
286
+ str
287
+ The examples for the Pointblank library.
288
+ """
289
+
290
+ sep_line = "-" * 70
291
+
292
+ examples_text = (
293
+ f"{sep_line}\nThis is a set of examples for the Pointblank library.\n{sep_line}\n\n"
294
+ )
295
+
296
+ # A large set of examples is available in the docs/demos directory, and each of the
297
+ # subdirectories contains a different example (in the form of a Quarto document)
298
+
299
+ example_dirs = [
300
+ "01-starter",
301
+ "02-advanced",
302
+ "03-data-extracts",
303
+ "04-sundered-data",
304
+ "05-step-report-column-check",
305
+ "06-step-report-schema-check",
306
+ "apply-checks-to-several-columns",
307
+ "check-row-column-counts",
308
+ "checks-for-missing",
309
+ "col-vals-custom-expr",
310
+ "column-selector-functions",
311
+ "comparisons-across-columns",
312
+ "expect-no-duplicate-rows",
313
+ "expect-no-duplicate-values",
314
+ "expect-text-pattern",
315
+ "failure-thresholds",
316
+ "mutate-table-in-step",
317
+ "numeric-comparisons",
318
+ "schema-check",
319
+ "set-membership",
320
+ "using-parquet-data",
321
+ ]
322
+
323
+ for example_dir in example_dirs:
324
+ link = f"https://posit-dev.github.io/pointblank/demos/{example_dir}/"
325
+
326
+ # Read in the index.qmd file for each example
327
+ with open(f"docs/demos/{example_dir}/index.qmd", "r") as f:
328
+ example_text = f.read()
329
+
330
+ # Remove the first eight lines of the example text (contains the YAML front matter)
331
+ example_text = "\n".join(example_text.split("\n")[8:])
332
+
333
+ # Extract the title of the example (the line beginning with `###`)
334
+ title = re.search(r"### (.*)", example_text).group(1)
335
+
336
+ # The next line with text is the short description of the example
337
+ desc = re.search(r"(.*)\.", example_text).group(1)
338
+
339
+ # Get all of the Python code blocks in the example
340
+ # these can be identified as starting with ```python and ending with ```
341
+ code_blocks = re.findall(r"```python\n(.*?)```", example_text, re.DOTALL)
342
+
343
+ # Wrap each code block with a leading ```python and trailing ```
344
+ code_blocks = [f"```python\n{code}```" for code in code_blocks]
345
+
346
+ # Collapse all code blocks into a single string
347
+ code_text = "\n\n".join(code_blocks)
348
+
349
+ # Add the example title, description, and code to the examples text
350
+ examples_text += f"### {title} ({link})\n\n{desc}\n\n{code_text}\n\n"
351
+
352
+ return examples_text
353
+
354
+
355
+ def _get_api_and_examples_text() -> str:
356
+ """
357
+ Get the combined API and examples text for the Pointblank library.
358
+
359
+ Returns
360
+ -------
361
+ str
362
+ The combined API and examples text for the Pointblank library.
363
+ """
364
+
365
+ api_text = _get_api_text()
366
+ examples_text = _get_examples_text()
367
+
368
+ return f"{api_text}\n\n{examples_text}"
369
+
370
+
371
+ def scrape_examples_index(base_url: str = "https://posit-dev.github.io/pointblank/") -> list[dict]:
372
+ """
373
+ Parse the examples index page from local .qmd file to extract demo titles and descriptions.
374
+
375
+ Parameters
376
+ ----------
377
+ base_url : str
378
+ The base URL of the Pointblank documentation site.
379
+
380
+ Returns
381
+ -------
382
+ list[dict]
383
+ A list of dictionaries with 'title', 'description', and 'url' keys.
384
+ """
385
+ examples = []
386
+
387
+ # Read from local file
388
+ qmd_path = Path(__file__).parent.parent / "docs" / "demos" / "index.qmd"
389
+
390
+ if not qmd_path.exists():
391
+ # Fallback to web scraping if local file doesn't exist
392
+ if not SCRAPING_AVAILABLE:
393
+ raise ImportError(
394
+ "requests is required for web scraping. Install it with: pip install requests"
395
+ )
396
+ demos_url = urljoin(base_url, "demos/")
397
+ response = requests.get(demos_url)
398
+ response.raise_for_status()
399
+ content = response.text
400
+ else:
401
+ with open(qmd_path, "r") as f:
402
+ content = f.read()
403
+
404
+ # Pattern to match the example structure in the .qmd file:
405
+ # [Title](./path/index.qmd)
406
+ # ... potentially an image ...
407
+ # <p ...>Description</p>
408
+
409
+ # First, get the grid-based examples with images
410
+ grid_pattern = r"\[([^\]]+)\]\(\./([^)]+)/index\.qmd\).*?<p[^>]*>(.*?)</p>"
411
+ matches = re.findall(grid_pattern, content, re.DOTALL)
412
+
413
+ for title, path, description in matches:
414
+ url = urljoin(base_url, f"demos/{path}/")
415
+ # Clean up description
416
+ desc_clean = re.sub(r"<[^>]+>", "", description).strip()
417
+ examples.append({"title": title.strip(), "description": desc_clean, "url": url})
418
+
419
+ # Also get the list-style examples (after the <hr>)
420
+ list_pattern = r"\[([^\]]+)\]\(\./([^)]+)/index\.qmd\)<br>\s*([^\n]+)"
421
+ list_matches = re.findall(list_pattern, content)
422
+
423
+ for title, path, description in list_matches:
424
+ url = urljoin(base_url, f"demos/{path}/")
425
+ examples.append({"title": title.strip(), "description": description.strip(), "url": url})
426
+
427
+ return examples
428
+
429
+
430
+ def scrape_api_reference_index(
431
+ base_url: str = "https://posit-dev.github.io/pointblank/",
432
+ ) -> list[dict]:
433
+ """
434
+ Parse the API reference index page from local .qmd file to extract function/class names and descriptions.
435
+
436
+ Parameters
437
+ ----------
438
+ base_url : str
439
+ The base URL of the Pointblank documentation site.
440
+
441
+ Returns
442
+ -------
443
+ list[dict]
444
+ A list of dictionaries with 'title', 'description', and 'url' keys.
445
+ """
446
+ api_items = []
447
+
448
+ # Read from local file
449
+ qmd_path = Path(__file__).parent.parent / "docs" / "reference" / "index.qmd"
450
+
451
+ if not qmd_path.exists():
452
+ # Fallback to web scraping if local file doesn't exist
453
+ if not SCRAPING_AVAILABLE:
454
+ raise ImportError(
455
+ "requests is required for web scraping. Install it with: pip install requests"
456
+ )
457
+ reference_url = urljoin(base_url, "reference/")
458
+ response = requests.get(reference_url)
459
+ response.raise_for_status()
460
+ content = response.text
461
+ else:
462
+ with open(qmd_path, "r") as f:
463
+ content = f.read()
464
+
465
+ # Pattern to match the API reference structure in the .qmd file:
466
+ # | [Function](path.qmd#anchor) | Description |
467
+
468
+ table_row_pattern = r"\| \[([^\]]+)\]\(([^)]+)\) \| ([^\|]+) \|"
469
+ matches = re.findall(table_row_pattern, content)
470
+
471
+ for title, path, description in matches:
472
+ # Extract just the filename without the anchor and change .qmd to .html
473
+ file_path = path.split("#")[0]
474
+ if file_path.endswith(".qmd"):
475
+ file_path = file_path[:-4] + ".html"
476
+ url = urljoin(base_url, f"reference/{file_path}")
477
+
478
+ api_items.append({"title": title.strip(), "description": description.strip(), "url": url})
479
+
480
+ return api_items
481
+
482
+
483
+ def generate_llms_txt(
484
+ base_url: str = "https://posit-dev.github.io/pointblank/",
485
+ include_user_guide: bool = True,
486
+ ) -> str:
487
+ """
488
+ Generate the llms.txt content for the Pointblank project.
489
+
490
+ Parameters
491
+ ----------
492
+ base_url : str
493
+ The base URL of the Pointblank documentation site.
494
+ include_user_guide : bool
495
+ Whether to include user guide pages in the output.
496
+
497
+ Returns
498
+ -------
499
+ str
500
+ The llms.txt formatted content.
501
+ """
502
+ if not SCRAPING_AVAILABLE:
503
+ raise ImportError(
504
+ "requests is required for web scraping. Install it with: pip install requests"
505
+ )
506
+
507
+ lines = ["# Pointblank", "", "## Docs", ""]
508
+
509
+ # Add examples section
510
+ try:
511
+ examples = scrape_examples_index(base_url)
512
+ if examples:
513
+ lines.append("### Examples")
514
+ lines.append("")
515
+ for ex in examples:
516
+ desc = f": {ex['description']}" if ex["description"] else ""
517
+ lines.append(f"- [{ex['title']}]({ex['url']}){desc}")
518
+ lines.append("")
519
+ except Exception as e:
520
+ print(f"Warning: Failed to scrape examples index: {e}")
521
+
522
+ # Add API reference section
523
+ try:
524
+ api_items = scrape_api_reference_index(base_url)
525
+ if api_items:
526
+ lines.append("### API Reference")
527
+ lines.append("")
528
+ for item in api_items:
529
+ desc = f": {item['description']}" if item["description"] else ""
530
+ lines.append(f"- [{item['title']}]({item['url']}){desc}")
531
+ lines.append("")
532
+ except Exception as e:
533
+ print(f"Warning: Failed to scrape API reference: {e}")
534
+
535
+ # If user guide is requested, scrape it too
536
+ if include_user_guide:
537
+ try:
538
+ user_guide_items = scrape_user_guide_index(base_url)
539
+ if user_guide_items:
540
+ lines.append("### User Guide")
541
+ lines.append("")
542
+ for item in user_guide_items:
543
+ desc = f": {item['description']}" if item["description"] else ""
544
+ lines.append(f"- [{item['title']}]({item['url']}){desc}")
545
+ except Exception as e:
546
+ print(f"Warning: Failed to scrape user guide: {e}")
547
+
548
+ return "\n".join(lines)
549
+
550
+
551
+ def scrape_user_guide_index(
552
+ base_url: str = "https://posit-dev.github.io/pointblank/",
553
+ ) -> list[dict]:
554
+ """
555
+ Get the user guide pages from local directory listing.
556
+
557
+ Parameters
558
+ ----------
559
+ base_url : str
560
+ The base URL of the Pointblank documentation site.
561
+
562
+ Returns
563
+ -------
564
+ list[dict]
565
+ A list of dictionaries with 'title', 'description', and 'url' keys.
566
+ """
567
+ guide_items = []
568
+
569
+ # Read from local directory
570
+ user_guide_dir = Path(__file__).parent.parent / "docs" / "user-guide"
571
+
572
+ if not user_guide_dir.exists():
573
+ return guide_items
574
+
575
+ # Get all .qmd files (excluding index.qmd)
576
+ qmd_files = sorted([f for f in user_guide_dir.glob("*.qmd") if f.name != "index.qmd"])
577
+
578
+ for qmd_file in qmd_files:
579
+ # Read the file to extract title
580
+ with open(qmd_file, "r") as f:
581
+ content = f.read()
582
+
583
+ # Try to extract title from YAML frontmatter
584
+ title_match = re.search(r'^title:\s*["\']?([^"\'\n]+)["\']?', content, re.MULTILINE)
585
+ if title_match:
586
+ title = title_match.group(1).strip()
587
+ else:
588
+ # Fallback to filename
589
+ title = qmd_file.stem.replace("-", " ").title()
590
+
591
+ # Try to extract first paragraph as description (optional)
592
+ # Skip code blocks and look for first real content
593
+ description = ""
594
+
595
+ url = urljoin(base_url, f"user-guide/{qmd_file.stem}.html")
596
+
597
+ guide_items.append({"title": title, "description": description, "url": url})
598
+
599
+ return guide_items
600
+
601
+
602
+ def generate_llms_full_txt(output_path: Optional[str] = None) -> str:
603
+ """
604
+ Generate the llms-full.txt content using the existing api-docs.txt file or by generating
605
+ the API and examples text.
606
+
607
+ Parameters
608
+ ----------
609
+ output_path : str, optional
610
+ Path to save the generated content. If None, content is returned but not saved.
611
+
612
+ Returns
613
+ -------
614
+ str
615
+ The llms-full.txt formatted content.
616
+ """
617
+ # Try to use existing api-docs.txt first
618
+ api_docs_path = Path(__file__).parent / "data" / "api-docs.txt"
619
+
620
+ if api_docs_path.exists():
621
+ with open(api_docs_path, "r") as f:
622
+ content = f.read()
623
+ else:
624
+ # Generate the content
625
+ content = _get_api_and_examples_text()
626
+
627
+ if output_path:
628
+ with open(output_path, "w") as f:
629
+ f.write(content)
630
+
631
+ return content
632
+
633
+
634
+ def main():
635
+ """
636
+ Main function to generate both llms.txt and llms-full.txt files.
637
+ """
638
+ # Generate llms.txt
639
+ print("Generating llms.txt...")
640
+ try:
641
+ llms_content = generate_llms_txt()
642
+ llms_path = Path(__file__).parent.parent / "docs" / "llms.txt"
643
+ with open(llms_path, "w") as f:
644
+ f.write(llms_content)
645
+ print(f"✓ Generated {llms_path}")
646
+ except Exception as e:
647
+ print(f"✗ Failed to generate llms.txt: {e}")
648
+
649
+ # Generate llms-full.txt
650
+ print("\nGenerating llms-full.txt...")
651
+ try:
652
+ llms_full_path = Path(__file__).parent.parent / "docs" / "llms-full.txt"
653
+ generate_llms_full_txt(str(llms_full_path))
654
+ print(f"✓ Generated {llms_full_path}")
655
+ except Exception as e:
656
+ print(f"✗ Failed to generate llms-full.txt: {e}")
657
+
658
+
659
+ if __name__ == "__main__":
660
+ main()
pointblank/assistant.py CHANGED
@@ -55,7 +55,7 @@ def assistant(
55
55
  ----------
56
56
  model
57
57
  The model to be used. This should be in the form of `provider:model` (e.g.,
58
- `"anthropic:claude-3-5-sonnet-latest"`). Supported providers are `"anthropic"`, `"openai"`,
58
+ `"anthropic:claude-sonnet-4-5"`). Supported providers are `"anthropic"`, `"openai"`,
59
59
  `"ollama"`, and `"bedrock"`.
60
60
  data
61
61
  An optional data table to focus on during discussion with the PbA, which could be a