barunsaha commited on
Commit
fb44830
·
1 Parent(s): 4e29b9c

Add plotting tools

Browse files
Files changed (5) hide show
  1. Gradio_UI.py +1 -0
  2. README.md +19 -1
  3. app.py +13 -118
  4. requirements.txt +1 -0
  5. tools/diagrams.py +201 -0
Gradio_UI.py CHANGED
@@ -271,6 +271,7 @@ class GradioUI:
271
  - List all the countries that are there.
272
  - How many distinct schools are there in each country? Show as a table. Sort by the country names.
273
  - Show the average download speed and latency of each school in Kenya.
 
274
  '''
275
  gr.Markdown(title)
276
 
 
271
  - List all the countries that are there.
272
  - How many distinct schools are there in each country? Show as a table. Sort by the country names.
273
  - Show the average download speed and latency of each school in Kenya.
274
+ - Show the count of schools in each country as a bar diagram.
275
  '''
276
  gr.Markdown(title)
277
 
README.md CHANGED
@@ -11,4 +11,22 @@ license: apache-2.0
11
  short_description: 'QoScope '
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  short_description: 'QoScope '
12
  ---
13
 
14
+ # QoScope 🔭
15
+
16
+ QoS data analysis thousands of school networks under UNICEF/Giga
17
+
18
+ ---
19
+
20
+
21
+ # Installation
22
+
23
+ ```bash
24
+ pip install -r requirements.txt
25
+ python app.py
26
+ ```
27
+
28
+ Add `HF_TOKEN` environment variable, e.g., in the `.env` file.
29
+
30
+ ---
31
+
32
+ QoScope is released under an Apache 2.0 license.
app.py CHANGED
@@ -1,14 +1,15 @@
1
- import tempfile
2
-
3
- import matplotlib.pyplot as plt
4
- import numpy as np
5
  import yaml
6
-
7
  import sqlalchemy as sqa
8
  from Gradio_UI import GradioUI
9
- from smolagents import CodeAgent, HfApiModel, load_tool, tool, Tool
 
 
10
  from tools.final_answer import FinalAnswerTool
11
 
 
12
  DATABASE_PATH = 'sqlite:///resampled_daily_avg.sqlite'
13
 
14
  db_engine = sqa.create_engine(DATABASE_PATH, echo=False)
@@ -20,7 +21,8 @@ def run_sql_query(sql_query: str) -> str:
20
  Run a SQL query on a table in a SQLite database and return the output/result as a string.
21
  The output contains one row of result(s) in each line.
22
  The output is simple text without any Markdown styling. An agent should take this plain output
23
- and format appropriately when required, e.g., as a Markdown table or summarizing the results in words.
 
24
 
25
  The only available table in the SQLite database is `school_measurements`.
26
  The table's CREATE statement (DDL) is as follows:
@@ -37,8 +39,8 @@ def run_sql_query(sql_query: str) -> str:
37
  iso3_format TEXT
38
  )
39
 
40
- IMPORTANT: You will ONLY execute SELECT queries.
41
- You will NEVER execute any other types of SQL queries, e.g., INSERT, DELETE, DROP, and so on, which changes the database in any way.
42
 
43
  Args:
44
  sql_query: An appropriate, correct SQL query for a SQLite database
@@ -54,118 +56,11 @@ def run_sql_query(sql_query: str) -> str:
54
  rows = con.execute(sqa.text(sql_query))
55
  for row in rows:
56
  # Each row is a tuple
57
- print(f'\n\n>>>{row=}')
58
  output += '\n' + str(row)
59
 
60
  return output
61
 
62
 
63
- # @tool
64
- def plot_line_diagram(
65
- x_values,
66
- y_values_list,
67
- labels=None,
68
- title='Line Diagram',
69
- xlabel='X-axis',
70
- ylabel='Y-axis'
71
- ):
72
- """
73
- Plots a line diagram with one or more y-values.
74
-
75
- :param x_values: List of x-values.
76
- :param y_values_list: List of lists containing y-values. Each inner list represents a separate line.
77
- :param labels: List of labels for each line (optional).
78
- :param title: Title of the plot (default: 'Line Diagram').
79
- :param xlabel: Label for the X-axis (default: 'X-axis').
80
- :param ylabel: Label for the Y-axis (default: 'Y-axis').
81
- """
82
- plt.figure(figsize=(10, 6))
83
-
84
- for i, y_values in enumerate(y_values_list):
85
- label = labels[i] if labels and i < len(labels) else f'Line {i + 1}'
86
- plt.plot(x_values, y_values, label=label)
87
-
88
- plt.title(title)
89
- plt.xlabel(xlabel)
90
- plt.ylabel(ylabel)
91
- plt.legend()
92
- plt.grid(True)
93
- plt.show()
94
-
95
-
96
- @tool
97
- def plot_bar_diagram(
98
- x_values: list,
99
- y_values_list: list[list[int | float]],
100
- labels: list[str] | None = None,
101
- title: str = 'Bar Diagram',
102
- xlabel: str = 'X-axis',
103
- ylabel: str = 'Y-axis'
104
- ) -> str:
105
- """
106
- Plot a bar diagram with one or more y-values and save the image to a temporary file.
107
- Return the path to the saved image file. The path can be used to display the image.
108
-
109
- Args:
110
- x_values: List of x-values.
111
- y_values_list: List of lists containing y-values. Each inner list represents a separate set of bars.
112
- labels: List of labels for each set of bars (optional).
113
- title: Title of the plot (default: 'Bar Diagram').
114
- xlabel: Label for the X-axis (default: 'X-axis').
115
- ylabel: Label for the Y-axis (default: 'Y-axis').
116
-
117
- Returns:
118
- Path to the saved image file.
119
- """
120
- bar_width = 0.2
121
- n = len(y_values_list)
122
-
123
- # Set positions of bars on X axis
124
- r = [np.arange(len(x_values))]
125
- for i in range(1, n):
126
- r.append([x + bar_width for x in r[i - 1]])
127
-
128
- plt.figure(figsize=(10, 6))
129
-
130
- for i, y_values in enumerate(y_values_list):
131
- label = labels[i] if labels and i < len(labels) else f'Set {i + 1}'
132
- plt.bar(r[i], y_values, width=bar_width, label=label)
133
-
134
- # Adding xticks
135
- plt.xlabel(xlabel)
136
- plt.ylabel(ylabel)
137
- plt.title(title)
138
- plt.xticks(
139
- [r + bar_width * (n - 1) / 2 for r in range(len(x_values))],
140
- x_values,
141
- rotation=45,
142
- ha='right'
143
- )
144
- plt.legend()
145
- plt.grid(True, axis='y')
146
- plt.tight_layout()
147
-
148
- # Save the plot as an image file in a temporary directory
149
- temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
150
- plt.savefig(temp_file.name)
151
- plt.close()
152
-
153
- return temp_file.name
154
-
155
-
156
- # class TextToImageTool(Tool):
157
- # description = "This tool creates an image according to a prompt, which is a text description."
158
- # name = "image_generator"
159
- # inputs = {"prompt": {"type": "string", "description": "The image generator prompt. Don't hesitate to add details in the prompt to make the image look better, like 'high-res, photorealistic', etc."}}
160
- # output_type = "image"
161
- # model_sdxl = "black-forest-labs/FLUX.1-schnell"
162
- # client = InferenceClient(model_sdxl)
163
- #
164
- #
165
- # def forward(self, prompt):
166
- # return self.client.text_to_image(prompt)
167
-
168
-
169
  ### Main block ###
170
 
171
  final_answer = FinalAnswerTool()
@@ -176,14 +71,14 @@ code_model = HfApiModel(
176
  custom_role_conversions=None,
177
  )
178
 
179
- with open('prompts.yaml', 'r') as stream:
180
  prompt_templates = yaml.safe_load(stream)
181
 
182
  agent = CodeAgent(
183
  model=code_model,
184
  tools=[
185
  run_sql_query,
186
- plot_bar_diagram,
187
  final_answer,
188
  ],
189
  max_steps=6,
 
1
+ """
2
+ QoScope agent with tools.
3
+ """
 
4
  import yaml
 
5
  import sqlalchemy as sqa
6
  from Gradio_UI import GradioUI
7
+ from smolagents import CodeAgent, HfApiModel, tool
8
+
9
+ from tools.diagrams import PlotTool
10
  from tools.final_answer import FinalAnswerTool
11
 
12
+
13
  DATABASE_PATH = 'sqlite:///resampled_daily_avg.sqlite'
14
 
15
  db_engine = sqa.create_engine(DATABASE_PATH, echo=False)
 
21
  Run a SQL query on a table in a SQLite database and return the output/result as a string.
22
  The output contains one row of result(s) in each line.
23
  The output is simple text without any Markdown styling. An agent should take this plain output
24
+ and format appropriately when required, e.g., as a Markdown table or summarizing the results
25
+ in words.
26
 
27
  The only available table in the SQLite database is `school_measurements`.
28
  The table's CREATE statement (DDL) is as follows:
 
39
  iso3_format TEXT
40
  )
41
 
42
+ IMPORTANT: You will ONLY execute SELECT queries. You will NEVER execute any other types of
43
+ SQL queries, e.g., INSERT, DELETE, DROP, and so on, which changes the database in any way.
44
 
45
  Args:
46
  sql_query: An appropriate, correct SQL query for a SQLite database
 
56
  rows = con.execute(sqa.text(sql_query))
57
  for row in rows:
58
  # Each row is a tuple
 
59
  output += '\n' + str(row)
60
 
61
  return output
62
 
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  ### Main block ###
65
 
66
  final_answer = FinalAnswerTool()
 
71
  custom_role_conversions=None,
72
  )
73
 
74
+ with open('prompts.yaml', 'r', encoding='utf-8') as stream:
75
  prompt_templates = yaml.safe_load(stream)
76
 
77
  agent = CodeAgent(
78
  model=code_model,
79
  tools=[
80
  run_sql_query,
81
+ PlotTool(),
82
  final_answer,
83
  ],
84
  max_steps=6,
requirements.txt CHANGED
@@ -4,3 +4,4 @@ pandas
4
  SQLAlchemy
5
  gradio
6
  matplotlib
 
 
4
  SQLAlchemy
5
  gradio
6
  matplotlib
7
+ Pillow
tools/diagrams.py ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ import tempfile
3
+
4
+ import numpy as np
5
+ from PIL import Image
6
+ from PIL.ImageFile import ImageFile
7
+ from matplotlib import pyplot as plt
8
+ from smolagents.tools import Tool
9
+
10
+
11
+ def _plot_line_diagram(
12
+ x_values: list,
13
+ y_values_list: list[list[int | float]],
14
+ labels: list[str] | None = None,
15
+ title: str = 'Bar Diagram',
16
+ xlabel: str = 'X-axis',
17
+ ylabel: str = 'Y-axis'
18
+ ) -> str:
19
+ """
20
+ Plot a line diagram with one or more y-values and save the image to a temporary file.
21
+ Return the path to the saved image file.
22
+
23
+ :param x_values: List of x-values.
24
+ :param y_values_list: List of lists containing y-values.
25
+ Each inner list represents a separate line.
26
+ :param labels: List of labels for each line (optional).
27
+ :param title: Title of the plot (default: 'Line Diagram').
28
+ :param xlabel: Label for the X-axis (default: 'X-axis').
29
+ :param ylabel: Label for the Y-axis (default: 'Y-axis').
30
+ :return: Path to the saved image file.
31
+ """
32
+
33
+ plt.figure(figsize=(10, 6))
34
+
35
+ for i, y_values in enumerate(y_values_list):
36
+ label = labels[i] if labels and i < len(labels) else f'Line {i+1}'
37
+ plt.plot(x_values, y_values, label=label)
38
+
39
+ plt.title(title)
40
+ plt.xlabel(xlabel)
41
+ plt.ylabel(ylabel)
42
+ plt.legend()
43
+ plt.grid(True)
44
+
45
+ # Save the plot as an image file in a temporary directory
46
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
47
+ plt.savefig(temp_file.name)
48
+ plt.close()
49
+
50
+ return temp_file.name
51
+
52
+
53
+ def _plot_bar_diagram(
54
+ x_values: list,
55
+ y_values_list: list[list[int | float]],
56
+ labels: list[str] | None = None,
57
+ title: str = 'Bar Diagram',
58
+ xlabel: str = 'X-axis',
59
+ ylabel: str = 'Y-axis'
60
+ ) -> str:
61
+ """
62
+ Plot a bar diagram with one or more y-values and save the image to a temporary file.
63
+ Return the path to the saved image file.
64
+
65
+ :param x_values: List of x-values.
66
+ :param y_values_list: List of lists containing y-values.
67
+ Each inner list represents a separate set of bars.
68
+ :param labels: List of labels for each set of bars (optional).
69
+ :param title: Title of the plot (default: 'Bar Diagram').
70
+ :param xlabel: Label for the X-axis (default: 'X-axis').
71
+ :param ylabel: Label for the Y-axis (default: 'Y-axis').
72
+ :return: Path to the saved image file.
73
+ """
74
+
75
+ bar_width = 0.2
76
+ n = len(y_values_list)
77
+
78
+ # Set positions of bars on X axis
79
+ r = [np.arange(len(x_values))]
80
+ for i in range(1, n):
81
+ r.append([x + bar_width for x in r[i - 1]])
82
+
83
+ plt.figure(figsize=(10, 6))
84
+
85
+ for i, y_values in enumerate(y_values_list):
86
+ label = labels[i] if labels and i < len(labels) else f'Set {i + 1}'
87
+ plt.bar(r[i], y_values, width=bar_width, label=label)
88
+
89
+ # Adding xticks
90
+ plt.xlabel(xlabel)
91
+ plt.ylabel(ylabel)
92
+ plt.title(title)
93
+ plt.xticks(
94
+ [r + bar_width * (n - 1) / 2 for r in range(len(x_values))],
95
+ x_values,
96
+ rotation=45,
97
+ ha='right'
98
+ )
99
+ plt.legend()
100
+ plt.grid(True, axis='y')
101
+ plt.tight_layout()
102
+
103
+ # Save the plot as an image file in a temporary directory
104
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix='.png')
105
+ plt.savefig(temp_file.name)
106
+ plt.close()
107
+
108
+ return temp_file.name
109
+
110
+
111
+ class PlotTool(Tool):
112
+ """
113
+ A tool to plot bar and line diagrams and return the images.
114
+ """
115
+
116
+ name = 'plot_bar_line_diagrams'
117
+ description = (
118
+ 'Plot a bar or line diagram with one or more y-values and save the image.'
119
+ ' Return the saved image file as `ImageFile`.'
120
+ ' An agent must take this `ImageFile` and display the image.'
121
+ )
122
+ inputs = {
123
+ 'plot_type': {
124
+ 'type': 'string',
125
+ 'description': 'The type of plot. Only two values are valid: `bar` and `line`'
126
+ },
127
+ 'x_values': {'type': 'array', 'description': 'List of x-values.'},
128
+ 'y_values_list': {
129
+ 'type': 'array',
130
+ 'description': (
131
+ 'A list of lists containing y-values (numbers).'
132
+ ' Each inner list represents a separate set of bars.'
133
+ ' The input type is list[list[int | float]]'
134
+ )
135
+ },
136
+ 'labels': {
137
+ 'type': 'array',
138
+ 'nullable': True,
139
+ 'description': (
140
+ 'A list of labels for each set of bars (optional). Defaults to `None`.'
141
+ ' If provided, the length of `labels` must be equal to the length of `x_values`.'
142
+ )
143
+ },
144
+ 'title': {
145
+ 'type': 'string',
146
+ 'nullable': True,
147
+ 'description': 'Title of the plot (default: "Bar Diagram")'
148
+ },
149
+ 'xlabel': {
150
+ 'type': 'string',
151
+ 'nullable': True,
152
+ 'description': 'Label for the X-axis (default: "X-axis")'
153
+ },
154
+ 'ylabel': {
155
+ 'type': 'string',
156
+ 'nullable': True,
157
+ 'description': 'Label for the Y-axis (default: "Y-axis").'
158
+ },
159
+ }
160
+ output_type = 'image'
161
+
162
+ def __init__(self, **kwargs):
163
+ super().__init__()
164
+
165
+ def forward(
166
+ self,
167
+ plot_type: str,
168
+ x_values: list,
169
+ y_values_list: list[list[int | float]],
170
+ labels: list[str] | None = None,
171
+ title: str = 'Bar Diagram',
172
+ xlabel: str = 'X-axis',
173
+ ylabel: str = 'Y-axis'
174
+ ) -> ImageFile:
175
+
176
+ if plot_type == 'bar':
177
+ img_file_name = _plot_bar_diagram(
178
+ x_values=x_values,
179
+ y_values_list=y_values_list,
180
+ labels=labels,
181
+ xlabel=xlabel,
182
+ ylabel=ylabel,
183
+ title=title
184
+ )
185
+ elif plot_type == 'line':
186
+ img_file_name = _plot_line_diagram(
187
+ x_values=x_values,
188
+ y_values_list=y_values_list,
189
+ labels=labels,
190
+ xlabel=xlabel,
191
+ ylabel=ylabel,
192
+ title=title
193
+ )
194
+ else:
195
+ img_file_name = None
196
+
197
+ if img_file_name:
198
+ with open(img_file_name, 'rb') as in_file:
199
+ return Image.open(io.BytesIO(in_file.read()))
200
+ else:
201
+ return None