Spaces:
Sleeping
Sleeping
mriusero
commited on
Commit
·
dc1621b
1
Parent(s):
ddf8386
feat: improve tools
Browse files- .gitignore +3 -7
- prompt.md +2 -2
- src/inference.py +3 -3
- src/tools/__init__.py +1 -1
- src/tools/analyze_chess.py +22 -12
- src/tools/analyze_document.py +1 -1
- src/tools/analyze_excel.py +17 -4
- src/tools/analyze_youtube_video.py +30 -9
- src/tools/calculator.py +12 -53
- src/tools/web_search.py +2 -2
- src/utils/tooling.py +6 -0
- tools.json +6 -11
.gitignore
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# OS
|
2 |
-
.DS_Store
|
3 |
|
4 |
# IDE
|
5 |
.idea/
|
@@ -13,11 +13,7 @@ my-traffic-analysis-441217-32bda1474a0f.json
|
|
13 |
|
14 |
# Model
|
15 |
llm/
|
16 |
-
|
17 |
logs/
|
18 |
1st_run/
|
19 |
-
|
20 |
-
metadata.jsonl
|
21 |
-
|
22 |
-
# Utils
|
23 |
-
agents_final_questions.json
|
|
|
1 |
# OS
|
2 |
+
.DS_Store
|
3 |
|
4 |
# IDE
|
5 |
.idea/
|
|
|
13 |
|
14 |
# Model
|
15 |
llm/
|
16 |
+
attachments/
|
17 |
logs/
|
18 |
1st_run/
|
19 |
+
metadata.jsonl
|
|
|
|
|
|
|
|
prompt.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
-
You are a general AI assistant. I will ask you a question.
|
2 |
Report your thoughts, and finish
|
3 |
your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER].
|
4 |
If a tool provide an error, use the tool differently.
|
5 |
-
|
6 |
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of
|
7 |
numbers and/or strings.
|
8 |
If you are asked for a number, don’t use comma to write your number neither use units such as $ or percent
|
|
|
1 |
+
You are a general and precise AI assistant. I will ask you a question.
|
2 |
Report your thoughts, and finish
|
3 |
your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER].
|
4 |
If a tool provide an error, use the tool differently.
|
5 |
+
For web searching, ensure your answer by cross-checking data with several sources.
|
6 |
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of
|
7 |
numbers and/or strings.
|
8 |
If you are asked for a number, don’t use comma to write your number neither use units such as $ or percent
|
src/inference.py
CHANGED
@@ -18,7 +18,7 @@ from src.tools import (
|
|
18 |
execute_code,
|
19 |
analyze_excel,
|
20 |
analyze_youtube_video,
|
21 |
-
|
22 |
)
|
23 |
|
24 |
load_dotenv()
|
@@ -42,7 +42,7 @@ class Agent:
|
|
42 |
"execute_code": execute_code,
|
43 |
"analyze_excel": analyze_excel,
|
44 |
"analyze_youtube_video": analyze_youtube_video,
|
45 |
-
"
|
46 |
}
|
47 |
self.log = []
|
48 |
self.tools = self.get_tools()
|
@@ -73,7 +73,7 @@ class Agent:
|
|
73 |
execute_code,
|
74 |
analyze_excel,
|
75 |
analyze_youtube_video,
|
76 |
-
|
77 |
]
|
78 |
).get('tools')
|
79 |
|
|
|
18 |
execute_code,
|
19 |
analyze_excel,
|
20 |
analyze_youtube_video,
|
21 |
+
calculate_sum,
|
22 |
)
|
23 |
|
24 |
load_dotenv()
|
|
|
42 |
"execute_code": execute_code,
|
43 |
"analyze_excel": analyze_excel,
|
44 |
"analyze_youtube_video": analyze_youtube_video,
|
45 |
+
"calculate_sum": calculate_sum,
|
46 |
}
|
47 |
self.log = []
|
48 |
self.tools = self.get_tools()
|
|
|
73 |
execute_code,
|
74 |
analyze_excel,
|
75 |
analyze_youtube_video,
|
76 |
+
calculate_sum,
|
77 |
]
|
78 |
).get('tools')
|
79 |
|
src/tools/__init__.py
CHANGED
@@ -9,4 +9,4 @@ from .transcribe_audio import transcribe_audio
|
|
9 |
from .execute_code import execute_code
|
10 |
from .analyze_excel import analyze_excel
|
11 |
from .analyze_youtube_video import analyze_youtube_video
|
12 |
-
from .calculator import
|
|
|
9 |
from .execute_code import execute_code
|
10 |
from .analyze_excel import analyze_excel
|
11 |
from .analyze_youtube_video import analyze_youtube_video
|
12 |
+
from .calculator import calculate_sum
|
src/tools/analyze_chess.py
CHANGED
@@ -1,19 +1,26 @@
|
|
1 |
-
import base64
|
2 |
-
import requests
|
3 |
-
from PIL import Image
|
4 |
-
from io import BytesIO
|
5 |
-
import chess.engine
|
6 |
from src.utils.tooling import tool
|
7 |
|
8 |
@tool
|
9 |
def analyze_chess(image_path: str) -> str:
|
10 |
"""
|
11 |
-
Analyzes a chess position from an image and
|
12 |
Args:
|
13 |
-
image_path (str): The path to the image file containing the chess
|
14 |
Returns:
|
15 |
-
str: The
|
16 |
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
def extract_fen_from_image(image_path):
|
18 |
return "5k2/ppp3pp/3b4/3P1n2/3q4/2N2Q2/PPP2PPP/4K3 b"
|
19 |
|
@@ -29,9 +36,12 @@ def analyze_chess(image_path: str) -> str:
|
|
29 |
if not is_valid_fen(fen):
|
30 |
raise ValueError(f"Invalid FEN: {fen}")
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
35 |
return result.move.uci()
|
36 |
|
37 |
fen = extract_fen_from_image(image_path)
|
@@ -41,4 +51,4 @@ def analyze_chess(image_path: str) -> str:
|
|
41 |
except ValueError as e:
|
42 |
return str(e)
|
43 |
|
44 |
-
return f"The FEN of the game is '5k2/ppp3pp/3b4/3P1n2/3q4/2N2Q2/PPP2PPP/4K3 b'"
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
from src.utils.tooling import tool
|
2 |
|
3 |
@tool
|
4 |
def analyze_chess(image_path: str) -> str:
|
5 |
"""
|
6 |
+
Analyzes a chess position from an image and return the game situation in FEN format.
|
7 |
Args:
|
8 |
+
image_path (str): The path to the image file containing the chess game.
|
9 |
Returns:
|
10 |
+
str: The FEN representation of the chess position.
|
11 |
"""
|
12 |
+
try:
|
13 |
+
import base64
|
14 |
+
import requests
|
15 |
+
from PIL import Image
|
16 |
+
from io import BytesIO
|
17 |
+
import chess.engine
|
18 |
+
|
19 |
+
except ImportError as e:
|
20 |
+
raise ImportError(
|
21 |
+
"You must install packages `markdownify` and `requests` to run this tool: for instance run `pip install markdownify requests`."
|
22 |
+
) from e
|
23 |
+
|
24 |
def extract_fen_from_image(image_path):
|
25 |
return "5k2/ppp3pp/3b4/3P1n2/3q4/2N2Q2/PPP2PPP/4K3 b"
|
26 |
|
|
|
36 |
if not is_valid_fen(fen):
|
37 |
raise ValueError(f"Invalid FEN: {fen}")
|
38 |
|
39 |
+
try:
|
40 |
+
engine = chess.engine.SimpleEngine.popen_uci("/opt/homebrew/bin/stockfish")
|
41 |
+
result = engine.play(chess.Board(fen), chess.engine.Limit(time=2.0))
|
42 |
+
engine.quit()
|
43 |
+
except Exception as e:
|
44 |
+
raise ValueError(f"Error communicating with chess engine in production (solution: `brew install stockfish`): {str(e)}")
|
45 |
return result.move.uci()
|
46 |
|
47 |
fen = extract_fen_from_image(image_path)
|
|
|
51 |
except ValueError as e:
|
52 |
return str(e)
|
53 |
|
54 |
+
return f"The FEN of the game is '5k2/ppp3pp/3b4/3P1n2/3q4/2N2Q2/PPP2PPP/4K3 b'.\nTips:\n1. Analyze all possibilities of next move\n2. List all of them\n3. Define the better one which guarantee a win."
|
src/tools/analyze_document.py
CHANGED
@@ -6,7 +6,7 @@ import re
|
|
6 |
def analyze_document(file_path: str, keywords: list) -> str:
|
7 |
"""
|
8 |
Extracts specific information from a local PDF or local text document based on given keywords.
|
9 |
-
(WARNING: This does not support URLs or web pages.)
|
10 |
Args:
|
11 |
file_path (str): The path to the PDF or text document to analyze.
|
12 |
keywords (list): A list of keywords to search for in the document.
|
|
|
6 |
def analyze_document(file_path: str, keywords: list) -> str:
|
7 |
"""
|
8 |
Extracts specific information from a local PDF or local text document based on given keywords.
|
9 |
+
(WARNING: This tool does not support URLs or web pages as input.)
|
10 |
Args:
|
11 |
file_path (str): The path to the PDF or text document to analyze.
|
12 |
keywords (list): A list of keywords to search for in the document.
|
src/tools/analyze_excel.py
CHANGED
@@ -21,10 +21,23 @@ def analyze_excel(file_path: str, sheet_name: str = None, specific_columns: list
|
|
21 |
if specific_columns:
|
22 |
df = df[specific_columns]
|
23 |
|
24 |
-
|
25 |
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
except FileNotFoundError:
|
30 |
return "File not found. Please check the file path."
|
@@ -33,4 +46,4 @@ def analyze_excel(file_path: str, sheet_name: str = None, specific_columns: list
|
|
33 |
except pd.errors.ParserError:
|
34 |
return "Error parsing the Excel file."
|
35 |
except Exception as e:
|
36 |
-
return f"An error occurred: {str(e)}"
|
|
|
21 |
if specific_columns:
|
22 |
df = df[specific_columns]
|
23 |
|
24 |
+
result = f"# Excel file loaded successfully !\n\n"
|
25 |
|
26 |
+
result += f"## Structure of the data\n * `{len(df)} rows`\n * `{len(df.columns)} columns`\n\n"
|
27 |
+
|
28 |
+
result += f"## Columns\n\n {', '.join(df.columns)}\n\n"
|
29 |
+
|
30 |
+
result += "## Raw table\n\n"
|
31 |
+
result += df.to_markdown(index=False)
|
32 |
+
result += "\n\n"
|
33 |
+
|
34 |
+
#result += "## Summary statistics\n\n"
|
35 |
+
#result += df.describe().to_markdown()
|
36 |
+
#result += "\n\n"
|
37 |
+
|
38 |
+
result += "## Recommendations\n\n(WARNING) Identify the columns that are relevant to your analysis before proceed to any calculus.\n\n"
|
39 |
+
|
40 |
+
return result
|
41 |
|
42 |
except FileNotFoundError:
|
43 |
return "File not found. Please check the file path."
|
|
|
46 |
except pd.errors.ParserError:
|
47 |
return "Error parsing the Excel file."
|
48 |
except Exception as e:
|
49 |
+
return f"An error occurred: {str(e)}"
|
src/tools/analyze_youtube_video.py
CHANGED
@@ -2,7 +2,7 @@ import os
|
|
2 |
import requests
|
3 |
from dotenv import load_dotenv
|
4 |
from isodate import parse_duration
|
5 |
-
|
6 |
from src.utils.tooling import tool
|
7 |
|
8 |
def extract_video_id(video_url: str) -> str:
|
@@ -31,12 +31,29 @@ def get_text(video_id: str, api_key: str) -> dict:
|
|
31 |
else:
|
32 |
raise Exception("Impossible to retrieve video details. Please check the video ID or API key.")
|
33 |
|
34 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
"""
|
36 |
Use Youtube API to get visual analysis of the video.
|
37 |
"""
|
38 |
return {
|
39 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
}
|
41 |
|
42 |
@tool
|
@@ -51,13 +68,17 @@ def analyze_youtube_video(video_url: str) -> dict:
|
|
51 |
|
52 |
video_id = extract_video_id(video_url)
|
53 |
text_details = get_text(video_id, api_key)
|
54 |
-
|
|
|
55 |
|
56 |
try:
|
57 |
-
result = f"
|
58 |
-
result += f"\
|
59 |
-
result += f"\
|
60 |
-
result += f"\
|
|
|
|
|
61 |
return result
|
|
|
62 |
except Exception as e:
|
63 |
-
return e
|
|
|
2 |
import requests
|
3 |
from dotenv import load_dotenv
|
4 |
from isodate import parse_duration
|
5 |
+
from youtube_transcript_api import YouTubeTranscriptApi
|
6 |
from src.utils.tooling import tool
|
7 |
|
8 |
def extract_video_id(video_url: str) -> str:
|
|
|
31 |
else:
|
32 |
raise Exception("Impossible to retrieve video details. Please check the video ID or API key.")
|
33 |
|
34 |
+
def get_transcript(video_id: str) -> str:
|
35 |
+
"""
|
36 |
+
Use youtube-transcript-api to get video transcript.
|
37 |
+
"""
|
38 |
+
try:
|
39 |
+
transcript = YouTubeTranscriptApi.get_transcript(video_id)
|
40 |
+
transcript_text = ' '.join([item['text'] for item in transcript])
|
41 |
+
return transcript_text
|
42 |
+
except Exception as e:
|
43 |
+
return f"Subtitles are disabled for this video, no transcript available."
|
44 |
+
|
45 |
+
def get_recommendations() -> dict:
|
46 |
"""
|
47 |
Use Youtube API to get visual analysis of the video.
|
48 |
"""
|
49 |
return {
|
50 |
+
'prompt': """
|
51 |
+
Provide a detailed analysis focusing on:
|
52 |
+
1. Main topic and key points from the title and description
|
53 |
+
2. Expected visual elements and scenes
|
54 |
+
3. Overall message or purpose
|
55 |
+
4. Target audience
|
56 |
+
"""
|
57 |
}
|
58 |
|
59 |
@tool
|
|
|
68 |
|
69 |
video_id = extract_video_id(video_url)
|
70 |
text_details = get_text(video_id, api_key)
|
71 |
+
transcript = get_transcript(video_id)
|
72 |
+
recommendations = get_recommendations()
|
73 |
|
74 |
try:
|
75 |
+
result = f"# YouTube Video Data Obtained Successfully !\n\n"
|
76 |
+
result += f"## Title\n'{text_details['title']}'\n\n"
|
77 |
+
result += f"## Description\n'{text_details['description']}'\n\n"
|
78 |
+
result += f"## Duration\n'{text_details['duration']} seconds'\n\n"
|
79 |
+
result += f"## Transcript\n'{transcript}'\n\n"
|
80 |
+
result += f"## Recommendations\n{recommendations['prompt']}\n\n"
|
81 |
return result
|
82 |
+
|
83 |
except Exception as e:
|
84 |
+
return e
|
src/tools/calculator.py
CHANGED
@@ -1,60 +1,19 @@
|
|
|
|
1 |
from src.utils.tooling import tool
|
2 |
-
import math
|
3 |
-
from typing import Union
|
4 |
|
5 |
@tool
|
6 |
-
def
|
7 |
"""
|
8 |
-
|
|
|
9 |
Args:
|
10 |
-
|
11 |
-
a (float): The first number or the angle in radians for trigonometric functions.
|
12 |
-
b (float, optional): The second number, required for operations 'add', 'subtract', 'multiply', 'divide', and 'power'.
|
13 |
Returns:
|
14 |
-
float: The
|
15 |
-
Raises:
|
16 |
-
ValueError: If the operation is not supported or if invalid arguments are provided.
|
17 |
"""
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
if b is None:
|
25 |
-
raise ValueError("Second number is required for subtraction.")
|
26 |
-
return a - b
|
27 |
-
|
28 |
-
elif operation == 'multiply':
|
29 |
-
if b is None:
|
30 |
-
raise ValueError("Second number is required for multiplication.")
|
31 |
-
return a * b
|
32 |
-
|
33 |
-
elif operation == 'divide':
|
34 |
-
if b is None:
|
35 |
-
raise ValueError("Second number is required for division.")
|
36 |
-
if b == 0:
|
37 |
-
raise ValueError("Cannot divide by zero.")
|
38 |
-
return a / b
|
39 |
-
|
40 |
-
elif operation == 'power':
|
41 |
-
if b is None:
|
42 |
-
raise ValueError("Exponent is required for power operation.")
|
43 |
-
return math.pow(a, b)
|
44 |
-
|
45 |
-
elif operation == 'sqrt':
|
46 |
-
if a < 0:
|
47 |
-
raise ValueError("Cannot calculate the square root of a negative number.")
|
48 |
-
return math.sqrt(a)
|
49 |
-
|
50 |
-
elif operation == 'sin':
|
51 |
-
return math.sin(a)
|
52 |
-
|
53 |
-
elif operation == 'cos':
|
54 |
-
return math.cos(a)
|
55 |
-
|
56 |
-
elif operation == 'tan':
|
57 |
-
return math.tan(a)
|
58 |
-
|
59 |
-
else:
|
60 |
-
raise ValueError(f"Unsupported operation: {operation}")
|
|
|
1 |
+
from typing import List
|
2 |
from src.utils.tooling import tool
|
|
|
|
|
3 |
|
4 |
@tool
|
5 |
+
def calculate_sum(numbers: list[float]) -> float:
|
6 |
"""
|
7 |
+
Calculates the sum of a list of numbers.
|
8 |
+
WARNING: You have to be sure that the input is coherent to answer correctly to a given question.
|
9 |
Args:
|
10 |
+
numbers (List[float]): A list of numbers to be summed from the question.
|
|
|
|
|
11 |
Returns:
|
12 |
+
float: The sum of the numbers.
|
|
|
|
|
13 |
"""
|
14 |
+
try:
|
15 |
+
total = sum(numbers)
|
16 |
+
return f"The sum of the list of number is: {total:.2f}"
|
17 |
+
except Exception as e:
|
18 |
+
print(f"Error calculating sum: {e}")
|
19 |
+
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/tools/web_search.py
CHANGED
@@ -5,7 +5,7 @@ def web_search(query: str, max_results: int = 3, timeout: int = 1) -> str:
|
|
5 |
"""
|
6 |
Performs a web search based on the query and returns the top search results.
|
7 |
Args:
|
8 |
-
query (str): The search query to perform.
|
9 |
max_results (int, optional): The maximum number of results to return. Defaults to 10.
|
10 |
timeout (int, optional): Timeout for the search request in seconds. Defaults to 10.
|
11 |
region (str, optional): Region code for the search. Defaults to 'wt-wt' (worldwide).
|
@@ -24,4 +24,4 @@ def web_search(query: str, max_results: int = 3, timeout: int = 1) -> str:
|
|
24 |
raise Exception("No results found! Try a less restrictive/shorter query.")
|
25 |
|
26 |
postprocessed_results = [f"[{result['title']}]({result['href']})\n{result['body']}" for result in results]
|
27 |
-
return "## Search Results\n\n" + "\n\n".join(postprocessed_results)
|
|
|
5 |
"""
|
6 |
Performs a web search based on the query and returns the top search results.
|
7 |
Args:
|
8 |
+
query (str): The search query to perform (Warning: have to be concise and precise).
|
9 |
max_results (int, optional): The maximum number of results to return. Defaults to 10.
|
10 |
timeout (int, optional): Timeout for the search request in seconds. Defaults to 10.
|
11 |
region (str, optional): Region code for the search. Defaults to 'wt-wt' (worldwide).
|
|
|
24 |
raise Exception("No results found! Try a less restrictive/shorter query.")
|
25 |
|
26 |
postprocessed_results = [f"[{result['title']}]({result['href']})\n{result['body']}" for result in results]
|
27 |
+
return "## Search Results\n\n" + "\n\n".join(postprocessed_results)
|
src/utils/tooling.py
CHANGED
@@ -46,6 +46,12 @@ def tool(func):
|
|
46 |
param_type = 'integer'
|
47 |
elif param_type == 'list':
|
48 |
param_type = 'array'
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
|
50 |
param_description = f"The {param_name}."
|
51 |
|
|
|
46 |
param_type = 'integer'
|
47 |
elif param_type == 'list':
|
48 |
param_type = 'array'
|
49 |
+
elif param_type == 'float':
|
50 |
+
param_type = 'number'
|
51 |
+
elif param_type == 'bool':
|
52 |
+
param_type = 'boolean'
|
53 |
+
elif param_type == 'dict':
|
54 |
+
param_type = 'object'
|
55 |
|
56 |
param_description = f"The {param_name}."
|
57 |
|
tools.json
CHANGED
@@ -9,7 +9,7 @@
|
|
9 |
"properties": {
|
10 |
"query": {
|
11 |
"type": "string",
|
12 |
-
"description": "The search query to perform."
|
13 |
},
|
14 |
"max_results": {
|
15 |
"type": "integer",
|
@@ -87,13 +87,13 @@
|
|
87 |
"type": "function",
|
88 |
"function": {
|
89 |
"name": "analyze_chess",
|
90 |
-
"description": "Analyzes a chess position from an image and
|
91 |
"parameters": {
|
92 |
"type": "object",
|
93 |
"properties": {
|
94 |
"image_path": {
|
95 |
"type": "string",
|
96 |
-
"description": "The path to the image file containing the chess
|
97 |
}
|
98 |
},
|
99 |
"required": [
|
@@ -238,22 +238,17 @@
|
|
238 |
{
|
239 |
"type": "function",
|
240 |
"function": {
|
241 |
-
"name": "
|
242 |
-
"description": "
|
243 |
"parameters": {
|
244 |
"type": "object",
|
245 |
"properties": {
|
246 |
-
"operation": {
|
247 |
-
"type": "string",
|
248 |
-
"description": "The operation to perform. Can be \"sum\", \"multiply\", \"subtract\", or \"divide\"."
|
249 |
-
},
|
250 |
"numbers": {
|
251 |
"type": "array",
|
252 |
-
"description": "A list of
|
253 |
}
|
254 |
},
|
255 |
"required": [
|
256 |
-
"operation",
|
257 |
"numbers"
|
258 |
]
|
259 |
}
|
|
|
9 |
"properties": {
|
10 |
"query": {
|
11 |
"type": "string",
|
12 |
+
"description": "The search query to perform (Warning: have to be concise and precise)."
|
13 |
},
|
14 |
"max_results": {
|
15 |
"type": "integer",
|
|
|
87 |
"type": "function",
|
88 |
"function": {
|
89 |
"name": "analyze_chess",
|
90 |
+
"description": "Analyzes a chess position from an image and return the game situation in FEN format.",
|
91 |
"parameters": {
|
92 |
"type": "object",
|
93 |
"properties": {
|
94 |
"image_path": {
|
95 |
"type": "string",
|
96 |
+
"description": "The path to the image file containing the chess game."
|
97 |
}
|
98 |
},
|
99 |
"required": [
|
|
|
238 |
{
|
239 |
"type": "function",
|
240 |
"function": {
|
241 |
+
"name": "calculate_sum",
|
242 |
+
"description": "Calculates the sum of a list of numbers.",
|
243 |
"parameters": {
|
244 |
"type": "object",
|
245 |
"properties": {
|
|
|
|
|
|
|
|
|
246 |
"numbers": {
|
247 |
"type": "array",
|
248 |
+
"description": "A list of numbers to be summed from the question."
|
249 |
}
|
250 |
},
|
251 |
"required": [
|
|
|
252 |
"numbers"
|
253 |
]
|
254 |
}
|