Skip to content

Commit ec43047

Browse files
authored
Api and chatbot components update (#107)
* ♻️ Refactor: modify apicall class signatute to add method and request_body as attributes and modify the scripts accordingly * 🐛 Fix: parse request body to make it compatible with request * 📝 Docs: update apicall config example * 📝 Docs: update README with link to Dockerfiles and image version t run vuegen with Docker * ♻️ Change chatbot class signature and make_api_request method from APICall class Define dynamic_request_body argument on the make_api_request to handle chatbot component instead of static request_body from config. Also, adapt chatbot class signature with these changes. * ♻️ Refactor(streamlit_report.py): modify the _generate_chatbot_content to handle a JSON file retrieved from the BKGR API * ♻️ Refactor: set model as optional parameter for chatbot class and modify the _generate_chatbot_content function to handle both Olla-style and standard API chatbots Also, fix bug in the _format_text function to recognize caption type * 🩹 Add black format * 📝 Docs: add detailed docstring fro the _generate_chatbot_content function and add chatbot case study on the README with chatbot information * 📝 Docs: add links to cong and folders for the case studies * 📝 Docs: update README to add infor about folder structure, --quartocheks argument, and tynitex installation for pdf reports * 🩹 Add balck format
1 parent 4939f02 commit ec43047

File tree

5 files changed

+289
-52
lines changed

5 files changed

+289
-52
lines changed

README.md

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,18 @@ quarto check
8383
> [!TIP]
8484
> If quarto is not installed, you can download the command-line interface from the [Quarto website][quarto-cli] for your operating system.
8585
86+
For PDF reports, you need to have a LaTeX distribution installed. This can be done with quarto using the following command:
87+
88+
```bash
89+
quarto install tinytex
90+
```
91+
92+
> [!TIP]
93+
> Also, you can add the `--quarto_checks` argument to the VueGen command to check and install the required dependencies automatically.
94+
8695
### Docker
8796

88-
If you prefer not to install VueGen on your system, a pre-configured Docker container is available. It includes all dependencies, ensuring a fully reproducible execution environment. See the [Execution section](#execution) for details on running VueGen with Docker. The official Docker image is available at [quay.io/dtu_biosustain_dsp/vuegen][vuegen-docker-quay].
97+
If you prefer not to install VueGen on your system, a pre-configured Docker container is available. It includes all dependencies, ensuring a fully reproducible execution environment. See the [Execution section](#execution) for details on running VueGen with Docker. The official Docker images are available at [quay.io/dtu_biosustain_dsp/vuegen][vuegen-docker-quay]. The Dockerfiles to build the images are available [here][docker-folder].
8998

9099
### Nextflow and nf-core
91100

@@ -94,7 +103,7 @@ VueGen is also available as a [nf-core][nfcore] module, customised for compatibi
94103
## Execution
95104

96105
> [!IMPORTANT]
97-
> Here we use the `Earth_microbiome_vuegen_demo_notebook` directory and the `Earth_microbiome_vuegen_demo_notebook.yaml` configuration file as examples, which are available in the `docs/example_data` and `docs/example_config_files` folders, respectively. Make sure to clone this reposiotry to access these contents, or use your own directory and configuration file.
106+
> Here we use the `Earth_microbiome_vuegen_demo_notebook` [directory][emp-dir] and the `Earth_microbiome_vuegen_demo_notebook.yaml` [configuration file][emp-config] as examples, which are available in the `docs/example_data` and `docs/example_config_files` folders, respectively. Make sure to clone this reposiotry to access these contents, or use your own directory and configuration file.
98107
99108
Run VueGen using a directory with the following command:
100109

@@ -105,12 +114,41 @@ vuegen --directory docs/example_data/Earth_microbiome_vuegen_demo_notebook --rep
105114
> [!NOTE]
106115
> By default, the `streamlit_autorun` argument is set to False, but you can use it in case you want to automatically run the streamlit app.
107116
117+
### Folder structure
118+
119+
Your input directory must follow a **nested folder structure**, where first-level folders are treated as **sections** and second-level folders as **subsections**, containing the components (plots, tables, networks, Markdown text, and HTML files).
120+
121+
Here is an example layout:
122+
```
123+
report_folder/
124+
├── section1/
125+
│ └── subsection1/
126+
│ ├── table.csv
127+
│ ├── image1.png
128+
│ └── chart.json
129+
├── section2/
130+
│ ├── subsection1/
131+
│ │ ├── summary_table.xls
132+
│ │ └── network_plot.graphml
133+
│ └── subsection2/
134+
│ ├── report.html
135+
│ └── summary.md
136+
```
137+
138+
> [!WARNING]
139+
> VueGen currently requires each section to contain at least one subsection folder. Defining only sections (with no subsections) or using deeper nesting levels (i.e., sub-subsections) will result in errors. In upcoming releases, we plan to support more flexible directory structures.
140+
141+
The titles for sections, subsections, and components are extracted from the corresponding folder and file names, and afterward, users can add descriptions, captions, and other details to the configuration file. Component types are inferred from the file extensions and names.
142+
The order of sections, subsections, and components can be defined using numerical suffixes in folder and file names.
143+
108144
It's also possible to provide a configuration file instead of a directory:
109145

110146
```bash
111147
vuegen --config docs/example_config_files/Earth_microbiome_vuegen_demo_notebook.yaml --report_type streamlit
112148
```
113149

150+
If a configuration file is given, users can specify titles and descriptions for sections and subsections, as well as component paths and required attributes, such as file format and delimiter for dataframes, plot types, and other details.
151+
114152
The current report types supported by VueGen are:
115153

116154
- Streamlit
@@ -130,7 +168,7 @@ Instead of installing VueGen locally, you can run it directly from a Docker cont
130168
docker run --rm \
131169
-v "$(pwd)/docs/example_data/Earth_microbiome_vuegen_demo_notebook:/home/appuser/Earth_microbiome_vuegen_demo_notebook" \
132170
-v "$(pwd)/output_docker:/home/appuser/streamlit_report" \
133-
quay.io/dtu_biosustain_dsp/vuegen:v0.3.1-docker --directory /home/appuser/Earth_microbiome_vuegen_demo_notebook --report_type streamlit
171+
quay.io/dtu_biosustain_dsp/vuegen:v0.3.2-docker --directory /home/appuser/Earth_microbiome_vuegen_demo_notebook --report_type streamlit
134172
```
135173

136174
## GUI
@@ -201,6 +239,9 @@ This introductory case study uses a predefined directory with plots, dataframes,
201239

202240
🔗 [![Open in Colab][colab_badge]][colab_link_intro_demo]
203241

242+
> [!NOTE]
243+
> The [configuration file][predef-dir-config] is available in the `docs/example_config_files` folder, and the [directory][predef-dir] with example data is in the `docs/example_data` folder.
244+
204245
**2. Earth Microbiome Project Data**
205246

206247
This advanced case study demonstrates the application of VueGen in a real-world scenario using data from the [Earth Microbiome Project (EMP)][emp]. The EMP is an initiative to characterize global microbial taxonomic and functional diversity. The notebook process the EMP data, create plots, dataframes, and other components, and organize outputs within a directory to produce reports. Report content and structure can be adapted by modifying the configuration file. Each report consists of sections on exploratory data analysis, metagenomics, and network analysis.
@@ -209,6 +250,36 @@ This advanced case study demonstrates the application of VueGen in a real-world
209250

210251
> [!NOTE]
211252
> The EMP case study is available online as [HTML][emp-html-demo] and [Streamlit][emp-st-demo] reports.
253+
> The [configuration file][emp-config] is available in the `docs/example_config_files` folder, and the [directory][emp-dir] with example data is in the `docs/example_data` folder.
254+
255+
**3. ChatBot Component**
256+
257+
This case study highlights VueGen’s capability to embed a chatbot component into a report subsection,
258+
enabling interactive conversations inside the report.
259+
260+
Two API modes are supported:
261+
262+
- **Ollama-style streaming chat completion**
263+
If a `model` parameter is specified in the config file, VueGen assumes the chatbot is using Ollama’s [/api/chat endpoint][ollama_chat].
264+
Messages are handled as chat history, and the assistant responses are streamed in real time for a smooth and responsive experience.
265+
This mode supports LLMs such as `llama3`, `deepsek`, or `mistral`.
266+
267+
> [!TIP]
268+
> See [Ollama’s website][ollama] for more details.
269+
270+
- **Standard prompt-response API**
271+
If no `model` is provided, VueGen uses a simpler prompt-response flow.
272+
A single prompt is sent to an endpoint, and a structured JSON object is expected in return.
273+
Currently, the response can include:
274+
- `text`: the main textual reply
275+
- `links`: a list of source URLs (optional)
276+
- `HTML content`: an HTML snippet with a Pyvis network visualization (optional)
277+
278+
This response structure is currently customized for an internal knowledge graph assistant, but VueGen is being actively developed
279+
to support more flexible and general-purpose response formats in future releases.
280+
281+
> [!NOTE]
282+
> You can see a [configuration file example][config-chatbot] for the chatbot component in the `docs/example_config_files` folder.
212283
213284
## Web application deployment
214285

@@ -261,13 +332,17 @@ We appreciate your feedback! If you have any comments, suggestions, or run into
261332
[vuegen-pypi]: https://pypi.org/project/vuegen/
262333
[vuegen-conda]: https://anaconda.org/bioconda/vuegen
263334
[vuegen-docker-quay]: https://quay.io/repository/dtu_biosustain_dsp/vuegen
335+
[docker-folder]: https://github.com/Multiomics-Analytics-Group/nf-vuegen/tree/main/Docker
264336
[vuegen-license]: https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/LICENSE
265337
[vuegen-class-diag-att]: https://raw.githubusercontent.com/Multiomics-Analytics-Group/vuegen/main/docs/images/vuegen_classdiagram_withattmeth.pdf
266338
[vuegen-docs]: https://vuegen.readthedocs.io/
267339
[ci-gh-action]: https://github.com/Multiomics-Analytics-Group/vuegen/actions/workflows/cdci.yml
268340
[ci-docs]: https://github.com/Multiomics-Analytics-Group/vuegen/actions/workflows/docs.yml
269341
[emp-html-demo]: https://multiomics-analytics-group.github.io/vuegen/
270342
[emp-st-demo]: https://earth-microbiome-vuegen-demo.streamlit.app/
343+
[ollama_chat]: https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion
344+
[ollama]: https://ollama.com/
345+
[config-chatbot]: https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_config_files/Chatbot_example_config.yaml
271346
[issues]: https://github.com/Multiomics-Analytics-Group/vuegen/issues
272347
[pulls]: https://github.com/Multiomics-Analytics-Group/vuegen/pulls
273348
[vuegen-preprint]: https://doi.org/10.1101/2025.03.05.641152
@@ -279,8 +354,12 @@ We appreciate your feedback! If you have any comments, suggestions, or run into
279354
[nf-vuegen]: https://github.com/Multiomics-Analytics-Group/nf-vuegen/
280355
[colab_badge]: https://colab.research.google.com/assets/colab-badge.svg
281356
[colab_link_intro_demo]: https://colab.research.google.com/github/Multiomics-Analytics-Group/vuegen/blob/main/docs/vuegen_basic_case_study.ipynb
357+
[predef-dir-config]: https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_config_files/Basic_example_vuegen_demo_notebook_config.yaml
358+
[predef-dir]: https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_data/Basic_example_vuegen_demo_notebook
282359
[colab_link_emp_demo]: https://colab.research.google.com/github/Multiomics-Analytics-Group/vuegen/blob/main/docs/vuegen_case_study_earth_microbiome.ipynb
283360
[emp]: https://earthmicrobiome.org/
361+
[emp-config]: https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_config_files/Earth_microbiome_vuegen_demo_notebook_config
362+
[emp-dir]: https://github.com/Multiomics-Analytics-Group/vuegen/blob/main/docs/example_data/Earth_microbiome_vuegen_demo_notebook
284363
[st-cloud]: https://streamlit.io/cloud
285364
[stlite]: https://github.com/whitphx/stlite
286365
[st-forum-exe]: https://discuss.streamlit.io/t/streamlit-deployment-as-an-executable-file-exe-for-windows-macos-and-android/6812
Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,44 @@
11
report:
22
title: APICall example
3-
description: An APICall exaple.
3+
description: An APICall example.
44
sections:
55
- title: APICall test
66
subsections:
77
- title: JSONPlaceholder test
88
components:
9-
- title: JSONPlaceholder component
9+
- title: GET request
1010
component_type: apicall
1111
api_url: https://jsonplaceholder.typicode.com/todos/1
12+
method: GET
13+
- title: POST request
14+
component_type: apicall
15+
api_url: https://jsonplaceholder.typicode.com/todos
16+
method: POST
17+
request_body: |
18+
{
19+
"userId": 1,
20+
"title": "Go running",
21+
"completed": false
22+
}
23+
- title: PUT request
24+
component_type: apicall
25+
api_url: https://jsonplaceholder.typicode.com/todos/10
26+
method: PUT
27+
request_body: |
28+
{
29+
"userId": 1,
30+
"title": "Play the guitar",
31+
"completed": true
32+
}
33+
- title: PATCH request
34+
component_type: apicall
35+
api_url: https://jsonplaceholder.typicode.com/todos/10
36+
method: PATCH
37+
request_body: |
38+
{
39+
"title": "Go for a hike"
40+
}
41+
- title: DELETE request
42+
component_type: apicall
43+
api_url: https://jsonplaceholder.typicode.com/todos/10
44+
method: DELETE

src/vuegen/config_manager.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,13 +561,24 @@ def _create_apicall_component(self, component_data: dict) -> r.APICall:
561561
APICall
562562
An APICall object populated with the provided metadata.
563563
"""
564+
request_body = component_data.get("request_body")
565+
parsed_body = None
566+
if request_body:
567+
try:
568+
parsed_body = json.loads(request_body)
569+
except json.JSONDecodeError as e:
570+
self.logger.error(f"Failed to parse request_body JSON: {e}")
571+
raise ValueError(f"Invalid JSON in request_body: {e}")
572+
564573
return r.APICall(
565574
title=component_data["title"],
566575
logger=self.logger,
567576
api_url=component_data["api_url"],
577+
method=component_data["method"],
568578
caption=component_data.get("caption"),
569579
headers=component_data.get("headers"),
570580
params=component_data.get("params"),
581+
request_body=parsed_body,
571582
)
572583

573584
def _create_chatbot_component(self, component_data: dict) -> r.ChatBot:
@@ -588,7 +599,7 @@ def _create_chatbot_component(self, component_data: dict) -> r.ChatBot:
588599
title=component_data["title"],
589600
logger=self.logger,
590601
api_url=component_data["api_url"],
591-
model=component_data["model"],
602+
model=component_data.get("model"),
592603
caption=component_data.get("caption"),
593604
headers=component_data.get("headers"),
594605
params=component_data.get("params"),

src/vuegen/report.py

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -530,20 +530,26 @@ class APICall(Component):
530530
----------
531531
api_url : str
532532
The URL of the API to interact with.
533+
method : str
534+
HTTP method to use for the request ("GET", "POST", or "PUT"). The deafult is "GET".
533535
headers : Optional[dict]
534536
Headers to include in the API request (default is None).
535537
params : Optional[dict]
536538
Query parameters to include in the API request (default is None).
539+
request_body : Optional[dict]
540+
The request body for methods like POST or PUT (default is None).
537541
"""
538542

539543
def __init__(
540544
self,
541545
title: str,
542546
logger: logging.Logger,
543547
api_url: str,
548+
method: str = "GET",
544549
caption: str = None,
545550
headers: Optional[dict] = None,
546551
params: Optional[dict] = None,
552+
request_body: Optional[dict] = None,
547553
):
548554
super().__init__(
549555
title=title,
@@ -552,35 +558,52 @@ def __init__(
552558
caption=caption,
553559
)
554560
self.api_url = api_url
561+
self.method = method.upper()
555562
self.headers = headers or {}
556563
self.params = params or {}
564+
# NOTE: request_body is usually dynamically set before the call for POST/PUT
565+
# but we'll include it here if needed for values from a config file
566+
self.request_body = request_body or {}
557567

558568
def make_api_request(
559-
self, method: str, request_body: Optional[dict] = None
569+
self, dynamic_request_body: Optional[dict] = None
560570
) -> Optional[dict]:
561571
"""
562572
Sends an HTTP request to the specified API and returns the JSON response.
573+
It allows overriding the request body dynamically.
563574
564575
Parameters
565576
----------
566-
method : str
567-
HTTP method to use for the request.
568-
request_body : Optional[dict], optional
569-
The request body for POST or PUT methods (default is None).
577+
dynamic_request_body : Optional[dict]
578+
A dictionary to use as the JSON request body for this specific call.
579+
Overrides the instance's request_body if provided.
570580
571581
Returns
572582
-------
573583
response : Optional[dict]
574584
The JSON response from the API, or None if the request fails.
575585
"""
586+
request_body_to_send = (
587+
dynamic_request_body
588+
if dynamic_request_body is not None
589+
else self.request_body
590+
)
576591
try:
577-
self.logger.info(f"Making {method} request to API: {self.api_url}")
592+
self.logger.info(f"Making {self.method} request to API: {self.api_url}")
593+
self.logger.debug(f"Headers: {self.headers}")
594+
self.logger.debug(f"Params: {self.params}")
595+
578596
response = requests.request(
579-
method,
597+
self.method,
580598
self.api_url,
581599
headers=self.headers,
582600
params=self.params,
583-
json=request_body,
601+
# Validate the request body based on the method
602+
json=(
603+
request_body_to_send
604+
if self.method in ["POST", "PUT", "PATCH"] and request_body_to_send
605+
else None
606+
),
584607
)
585608
response.raise_for_status()
586609
self.logger.info(
@@ -599,10 +622,10 @@ class ChatBot(Component):
599622
600623
Attributes
601624
----------
602-
model : str
603-
The language model to use for the chatbot.
604625
api_call : APICall
605626
An instance of the APICall class used to interact with the API for fetching chatbot responses.
627+
model : Optional[str]
628+
The language model to use for the chatbot (default is None).
606629
headers : Optional[dict]
607630
Headers to include in the API request (default is None).
608631
params : Optional[dict]
@@ -614,8 +637,8 @@ def __init__(
614637
title: str,
615638
logger: logging.Logger,
616639
api_url: str,
617-
model: str,
618640
caption: str = None,
641+
model: Optional[str] = None,
619642
headers: Optional[dict] = None,
620643
params: Optional[dict] = None,
621644
):
@@ -630,7 +653,8 @@ def __init__(
630653
title=title,
631654
logger=logger,
632655
api_url=api_url,
633-
caption=caption,
656+
method="POST",
657+
caption=None,
634658
headers=headers,
635659
params=params,
636660
)

0 commit comments

Comments
 (0)