tmzh commited on
Commit
95bf5e4
1 Parent(s): a0193f9

working chat agent

Browse files
Files changed (6) hide show
  1. agent.py +82 -51
  2. functions.py +3 -1
  3. requirements.txt +1 -4
  4. templates/query.html +12 -0
  5. tool_use.ipynb +589 -0
  6. tools.py +4 -74
agent.py CHANGED
@@ -1,70 +1,101 @@
1
-
2
- import re
3
- import json
4
-
5
  import functools
 
 
 
 
6
  import functions
7
- import torch
8
  from tools import tools
9
- from transformers import (
10
- AutoModelForCausalLM,
11
- AutoTokenizer,
12
- BitsAndBytesConfig
13
- )
14
 
15
- # Get all functions from functions.py
 
 
 
 
 
16
  all_functions = [func for func in dir(functions) if callable(
17
  getattr(functions, func)) and not func.startswith("__")]
18
-
19
- # Create names_to_function dict containing partials for all functions in functions.py
20
  names_to_functions = {func: functools.partial(
21
  getattr(functions, func)) for func in all_functions}
22
 
23
 
24
- model_id = "meta-llama/Meta-Llama-3.1-8B-Instruct"
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- # specify how to quantize the model
27
- quantization_config = BitsAndBytesConfig(
28
- load_in_4bit=True,
29
- bnb_4bit_quant_type="nf4",
30
- bnb_4bit_compute_dtype=torch.bfloat16,
31
- )
32
 
33
- tokenizer = AutoTokenizer.from_pretrained(model_id)
34
- model = AutoModelForCausalLM.from_pretrained(
35
- model_id, device_map="auto", quantization_config=quantization_config
36
- )
 
 
 
 
 
 
 
37
 
38
 
39
- def extract_function_call(output):
40
- match = re.search(r'<\|python_tag\|>(.*)<\|eom_id\|>', output)
41
- if match:
42
- function_call = match.group(1)
43
- return json.loads(function_call)
 
 
 
44
  else:
45
  return None
46
 
47
 
48
- def chatbot(query):
49
- messages = [
50
- {"role": "system", "content": "You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer."},
51
- {"role": "user", "content": query},
52
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
- tokenized_chat = tokenizer.apply_chat_template(
55
- messages, tools=tools, add_generation_prompt=True, tokenize=True, return_tensors="pt")
56
-
57
- outputs = model.generate(tokenized_chat, max_new_tokens=128)
58
- answer = tokenizer.batch_decode(outputs[:, tokenized_chat.shape[1]:])[0]
59
- tool_call = extract_function_call(answer)
60
-
61
- if tool_call:
62
- function_name = tool_call['name']
63
- function_params = tool_call['parameters']
64
- print("\nfunction_name: ", function_name,
65
- "\nfunction_params: ", function_params)
66
- function_result = names_to_functions[function_name](**function_params)
67
- print(function_result['results'])
68
- return function_result['results']
69
- else:
70
- print("No tool calls found in the answer.")
 
 
 
 
 
1
  import functools
2
+ import json
3
+ import os
4
+ import logging
5
+ from groq import Groq
6
  import functions
 
7
  from tools import tools
 
 
 
 
 
8
 
9
+ # Set up logging
10
+ logging.basicConfig(level=logging.DEBUG)
11
+
12
+ client = Groq(api_key=os.environ["GROQ_API_KEY"])
13
+
14
+ MODEL = "llama3-groq-70b-8192-tool-use-preview"
15
  all_functions = [func for func in dir(functions) if callable(
16
  getattr(functions, func)) and not func.startswith("__")]
 
 
17
  names_to_functions = {func: functools.partial(
18
  getattr(functions, func)) for func in all_functions}
19
 
20
 
21
+ def create_message(prompt):
22
+ logging.debug(f"Creating message with prompt: {prompt}")
23
+ return [
24
+ {
25
+ "role": "system",
26
+ "content": "You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer.",
27
+ },
28
+ {
29
+ "role": "user",
30
+ "content": prompt,
31
+ },
32
+ ]
33
 
 
 
 
 
 
 
34
 
35
+ def get_response(client, model, messages, tool_choice="auto"):
36
+ logging.debug(
37
+ f"Getting response with model: {model}, messages: {messages}, tool_choice: {tool_choice}")
38
+ return client.chat.completions.create(
39
+ model=model,
40
+ messages=messages,
41
+ tools=tools,
42
+ tool_choice=tool_choice,
43
+ temperature=0,
44
+ max_tokens=4096,
45
+ )
46
 
47
 
48
+ def generate_reasoning_chain(messages):
49
+ logging.debug(f"Generating reasoning chain with messages: {messages}")
50
+ cot_response = get_response(client, MODEL, messages, tool_choice="none")
51
+ if cot_response.choices[0].finish_reason == "stop":
52
+ messages.append({
53
+ 'role': cot_response.choices[0].message.role,
54
+ 'content': cot_response.choices[0].message.content})
55
+ return messages
56
  else:
57
  return None
58
 
59
 
60
+ def gather_movie_data(messages, iteration=0, max_iterations=2):
61
+ logging.debug(
62
+ f"Gathering movie data with messages: {messages}, iteration: {iteration}")
63
+ response = get_response(client, MODEL, messages, tool_choice="required")
64
+ if response.choices[0].finish_reason == "tool_calls":
65
+ tool_calls = response.choices[0].message.tool_calls
66
+ updated_messages = messages.copy()
67
+ for tool_call in tool_calls:
68
+ tool_output = execute_tool(tool_call)
69
+ logging.debug(
70
+ f"Tool call: {tool_call.function.name}, output: {tool_output}")
71
+ if tool_call.function.name == "discover_movie":
72
+ return tool_output["results"] # A list of movies
73
+ else:
74
+ updated_messages.append(
75
+ {
76
+ "tool_call_id": tool_call.id,
77
+ "role": "tool",
78
+ "name": tool_call.function.name,
79
+ "content": str(tool_output),
80
+ }
81
+ )
82
+ if iteration < max_iterations:
83
+ return gather_movie_data(updated_messages, iteration + 1)
84
 
85
+
86
+ def execute_tool(tool_call):
87
+ logging.debug(f"Executing tool: {tool_call.function.name}")
88
+ function_to_call = names_to_functions[tool_call.function.name]
89
+ function_args = json.loads(tool_call.function.arguments)
90
+ return function_to_call(**function_args)
91
+
92
+
93
+ def chatbot(user_prompt):
94
+ messages = create_message(user_prompt)
95
+ cot = generate_reasoning_chain(messages)
96
+ movie_list = gather_movie_data(cot)
97
+ return movie_list
98
+
99
+
100
+ if __name__ == "__main__":
101
+ print(json.dumps(chatbot("List comedy movies with tom cruise in it"), indent=2))
functions.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import requests
2
  import os
3
  import logging
@@ -16,7 +17,8 @@ def query_tmdb(endpoint, params={}):
16
  }
17
  response = requests.get(endpoint, headers=headers, params=params)
18
  # Log the response
19
- logging.debug(f"Response from {endpoint}: {response.json()}")
 
20
 
21
  return response.json()
22
 
 
1
+ import json
2
  import requests
3
  import os
4
  import logging
 
17
  }
18
  response = requests.get(endpoint, headers=headers, params=params)
19
  # Log the response
20
+ logging.debug(
21
+ f"Response from {endpoint}:\n{json.dumps(response.json(), indent=4)}")
22
 
23
  return response.json()
24
 
requirements.txt CHANGED
@@ -1,6 +1,3 @@
1
  Flask
2
  requests
3
- transformers
4
- torch
5
- bitsandbytes
6
- accelerate
 
1
  Flask
2
  requests
3
+ groq==0.9.0
 
 
 
templates/query.html CHANGED
@@ -1,6 +1,7 @@
1
  {% extends 'base.html' %}
2
 
3
  {% block content %}
 
4
  <h1>Query</h1>
5
  <form action="/query" method="post">
6
  <input type="text" name="query" placeholder="Enter your query...">
@@ -20,4 +21,15 @@
20
  {% elif result is defined %}
21
  <p>No results found.</p>
22
  {% endif %}
 
 
 
 
 
 
 
 
 
 
 
23
  {% endblock %}
 
1
  {% extends 'base.html' %}
2
 
3
  {% block content %}
4
+
5
  <h1>Query</h1>
6
  <form action="/query" method="post">
7
  <input type="text" name="query" placeholder="Enter your query...">
 
21
  {% elif result is defined %}
22
  <p>No results found.</p>
23
  {% endif %}
24
+
25
+
26
+ <div id="overlay" onclick="closePanel()"></div> <!-- Overlay for closing panel -->
27
+ <div id="side-panel">
28
+ <img id="movie-poster" src="" alt="${movie.title} Poster" style="max-width: 150px;">
29
+ <div id="movie-details">
30
+ <!-- Movie details will be injected here -->
31
+ </div>
32
+ <span id="close-button" onclick="closePanel()">&times;</span>
33
+
34
+ </div>
35
  {% endblock %}
tool_use.ipynb ADDED
@@ -0,0 +1,589 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [
8
+ {
9
+ "name": "stdout",
10
+ "output_type": "stream",
11
+ "text": [
12
+ "hello\n"
13
+ ]
14
+ }
15
+ ],
16
+ "source": [
17
+ "print(\"hello\")"
18
+ ]
19
+ },
20
+ {
21
+ "cell_type": "code",
22
+ "execution_count": 2,
23
+ "metadata": {},
24
+ "outputs": [
25
+ {
26
+ "name": "stderr",
27
+ "output_type": "stream",
28
+ "text": [
29
+ "/home/tamizh/miniconda3/envs/movies-app/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
30
+ " from .autonotebook import tqdm as notebook_tqdm\n",
31
+ "DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): huggingface.co:443\n",
32
+ "DEBUG:urllib3.connectionpool:https://huggingface.co:443 \"HEAD /meta-llama/Meta-Llama-3.1-8B-Instruct/resolve/main/tokenizer_config.json HTTP/11\" 200 0\n",
33
+ "DEBUG:urllib3.connectionpool:https://huggingface.co:443 \"HEAD /meta-llama/Meta-Llama-3.1-8B-Instruct/resolve/main/config.json HTTP/11\" 200 0\n",
34
+ "DEBUG:bitsandbytes.cextension:Loading bitsandbytes native library from: /home/tamizh/miniconda3/envs/movies-app/lib/python3.11/site-packages/bitsandbytes/libbitsandbytes_cuda121.so\n",
35
+ "INFO:accelerate.utils.modeling:We will use 90% of the memory on device 0 for storing the model, and 10% for the buffer to avoid OOM. You can set `max_memory` in to a higher value to use more memory (at your own risk).\n",
36
+ "Loading checkpoint shards: 100%|██████████| 4/4 [00:07<00:00, 1.81s/it]\n",
37
+ "DEBUG:urllib3.connectionpool:https://huggingface.co:443 \"HEAD /meta-llama/Meta-Llama-3.1-8B-Instruct/resolve/main/generation_config.json HTTP/11\" 200 0\n"
38
+ ]
39
+ }
40
+ ],
41
+ "source": [
42
+ "import re\n",
43
+ "import json\n",
44
+ "\n",
45
+ "from functions import *\n",
46
+ "from transformers import pipeline\n",
47
+ "from tools import tools\n",
48
+ "\n",
49
+ "import functions\n",
50
+ "import torch\n",
51
+ "from transformers import (\n",
52
+ " AutoModelForCausalLM,\n",
53
+ " AutoTokenizer,\n",
54
+ " BitsAndBytesConfig\n",
55
+ ")\n",
56
+ "\n",
57
+ "from transformers import AutoTokenizer, AutoModelForCausalLM\n",
58
+ "\n",
59
+ "quantization_config = BitsAndBytesConfig(\n",
60
+ " load_in_8bit=True,\n",
61
+ " load_in_4bit=False,\n",
62
+ " bnb_4bit_quant_type=\"nf4\",\n",
63
+ " bnb_4bit_compute_dtype=torch.bfloat16\n",
64
+ ")\n",
65
+ "\n",
66
+ "model_id = \"meta-llama/Meta-Llama-3.1-8B-Instruct\"\n",
67
+ "tokenizer = AutoTokenizer.from_pretrained(model_id)\n",
68
+ "model = AutoModelForCausalLM.from_pretrained(model_id, \n",
69
+ " device_map=\"auto\", \n",
70
+ " quantization_config=quantization_config)"
71
+ ]
72
+ },
73
+ {
74
+ "cell_type": "code",
75
+ "execution_count": 5,
76
+ "metadata": {},
77
+ "outputs": [],
78
+ "source": [
79
+ "def generate_reasoning_chain(query):\n",
80
+ " user_message = f\"\"\"\n",
81
+ " Given the user query: \"{query}\"\n",
82
+ " Generate a multi-step reasoning chain to answer the query. Include steps for using available tools if necessary.\n",
83
+ " \"\"\"\n",
84
+ "\n",
85
+ " messages = [\n",
86
+ " {\"role\": \"system\", \"content\": \"You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer.\"},\n",
87
+ " {\"role\": \"user\", \"content\": user_message},\n",
88
+ " ]\n",
89
+ "\n",
90
+ " tokenized_chat = tokenizer.apply_chat_template(\n",
91
+ " messages, tools=tools, add_generation_prompt=False, tokenize=True, return_tensors=\"pt\")\n",
92
+ "\n",
93
+ "\n",
94
+ " outputs = model.generate(tokenized_chat, max_new_tokens=128)\n",
95
+ " # return tokenizer.batch_decode(outputs[:, tokenized_chat.shape[1]:])[0]\n",
96
+ " return tokenizer.batch_decode(outputs)[0]"
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "code",
101
+ "execution_count": 3,
102
+ "metadata": {},
103
+ "outputs": [
104
+ {
105
+ "name": "stdout",
106
+ "output_type": "stream",
107
+ "text": [
108
+ "{{- bos_token }}\n",
109
+ "{%- if custom_tools is defined %}\n",
110
+ " {%- set tools = custom_tools %}\n",
111
+ "{%- endif %}\n",
112
+ "{%- if not tools_in_user_message is defined %}\n",
113
+ " {%- set tools_in_user_message = true %}\n",
114
+ "{%- endif %}\n",
115
+ "{%- if not date_string is defined %}\n",
116
+ " {%- set date_string = \"26 Jul 2024\" %}\n",
117
+ "{%- endif %}\n",
118
+ "{%- if not tools is defined %}\n",
119
+ " {%- set tools = none %}\n",
120
+ "{%- endif %}\n",
121
+ "\n",
122
+ "{#- This block extracts the system message, so we can slot it into the right place. #}\n",
123
+ "{%- if messages[0]['role'] == 'system' %}\n",
124
+ " {%- set system_message = messages[0]['content']|trim %}\n",
125
+ " {%- set messages = messages[1:] %}\n",
126
+ "{%- else %}\n",
127
+ " {%- set system_message = \"\" %}\n",
128
+ "{%- endif %}\n",
129
+ "\n",
130
+ "{#- System message + builtin tools #}\n",
131
+ "{{- \"<|start_header_id|>system<|end_header_id|>\\n\\n\" }}\n",
132
+ "{%- if builtin_tools is defined or tools is not none %}\n",
133
+ " {{- \"Environment: ipython\\n\" }}\n",
134
+ "{%- endif %}\n",
135
+ "{%- if builtin_tools is defined %}\n",
136
+ " {{- \"Tools: \" + builtin_tools | reject('equalto', 'code_interpreter') | join(\", \") + \"\\n\\n\"}}\n",
137
+ "{%- endif %}\n",
138
+ "{{- \"Cutting Knowledge Date: December 2023\\n\" }}\n",
139
+ "{{- \"Today Date: \" + date_string + \"\\n\\n\" }}\n",
140
+ "{%- if tools is not none and not tools_in_user_message %}\n",
141
+ " {{- \"You have access to the following functions. To call a function, please respond with JSON for a function call.\" }}\n",
142
+ " {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n",
143
+ " {{- \"Do not use variables.\\n\\n\" }}\n",
144
+ " {%- for t in tools %}\n",
145
+ " {{- t | tojson(indent=4) }}\n",
146
+ " {{- \"\\n\\n\" }}\n",
147
+ " {%- endfor %}\n",
148
+ "{%- endif %}\n",
149
+ "{{- system_message }}\n",
150
+ "{{- \"<|eot_id|>\" }}\n",
151
+ "\n",
152
+ "{#- Custom tools are passed in a user message with some extra guidance #}\n",
153
+ "{%- if tools_in_user_message and not tools is none %}\n",
154
+ " {#- Extract the first user message so we can plug it in here #}\n",
155
+ " {%- if messages | length != 0 %}\n",
156
+ " {%- set first_user_message = messages[0]['content']|trim %}\n",
157
+ " {%- set messages = messages[1:] %}\n",
158
+ " {%- else %}\n",
159
+ " {{- raise_exception(\"Cannot put tools in the first user message when there's no first user message!\") }}\n",
160
+ "{%- endif %}\n",
161
+ " {{- '<|start_header_id|>user<|end_header_id|>\\n\\n' -}}\n",
162
+ " {{- \"Given the following functions, please respond with a JSON for a function call \" }}\n",
163
+ " {{- \"with its proper arguments that best answers the given prompt.\\n\\n\" }}\n",
164
+ " {{- 'Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.' }}\n",
165
+ " {{- \"Do not use variables.\\n\\n\" }}\n",
166
+ " {%- for t in tools %}\n",
167
+ " {{- t | tojson(indent=4) }}\n",
168
+ " {{- \"\\n\\n\" }}\n",
169
+ " {%- endfor %}\n",
170
+ " {{- first_user_message + \"<|eot_id|>\"}}\n",
171
+ "{%- endif %}\n",
172
+ "\n",
173
+ "{%- for message in messages %}\n",
174
+ " {%- if not (message.role == 'ipython' or message.role == 'tool' or 'tool_calls' in message) %}\n",
175
+ " {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\\n\\n'+ message['content'] | trim + '<|eot_id|>' }}\n",
176
+ " {%- elif 'tool_calls' in message %}\n",
177
+ " {%- if not message.tool_calls|length == 1 %}\n",
178
+ " {{- raise_exception(\"This model only supports single tool-calls at once!\") }}\n",
179
+ " {%- endif %}\n",
180
+ " {%- set tool_call = message.tool_calls[0].function %}\n",
181
+ " {%- if builtin_tools is defined and tool_call.name in builtin_tools %}\n",
182
+ " {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' -}}\n",
183
+ " {{- \"<|python_tag|>\" + tool_call.name + \".call(\" }}\n",
184
+ " {%- for arg_name, arg_val in tool_call.arguments | items %}\n",
185
+ " {{- arg_name + '=\"' + arg_val + '\"' }}\n",
186
+ " {%- if not loop.last %}\n",
187
+ " {{- \", \" }}\n",
188
+ " {%- endif %}\n",
189
+ " {%- endfor %}\n",
190
+ " {{- \")\" }}\n",
191
+ " {%- else %}\n",
192
+ " {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' -}}\n",
193
+ " {{- '{\"name\": \"' + tool_call.name + '\", ' }}\n",
194
+ " {{- '\"parameters\": ' }}\n",
195
+ " {{- tool_call.arguments | tojson }}\n",
196
+ " {{- \"}\" }}\n",
197
+ " {%- endif %}\n",
198
+ " {%- if builtin_tools is defined %}\n",
199
+ " {#- This means we're in ipython mode #}\n",
200
+ " {{- \"<|eom_id|>\" }}\n",
201
+ " {%- else %}\n",
202
+ " {{- \"<|eot_id|>\" }}\n",
203
+ " {%- endif %}\n",
204
+ " {%- elif message.role == \"tool\" or message.role == \"ipython\" %}\n",
205
+ " {{- \"<|start_header_id|>ipython<|end_header_id|>\\n\\n\" }}\n",
206
+ " {%- if message.content is mapping or message.content is iterable %}\n",
207
+ " {{- message.content | tojson }}\n",
208
+ " {%- else %}\n",
209
+ " {{- message.content }}\n",
210
+ " {%- endif %}\n",
211
+ " {{- \"<|eot_id|>\" }}\n",
212
+ " {%- endif %}\n",
213
+ "{%- endfor %}\n",
214
+ "{%- if add_generation_prompt %}\n",
215
+ " {{- '<|start_header_id|>assistant<|end_header_id|>\\n\\n' }}\n",
216
+ "{%- endif %}\n",
217
+ "\n"
218
+ ]
219
+ }
220
+ ],
221
+ "source": [
222
+ "print(tokenizer.chat_template)"
223
+ ]
224
+ },
225
+ {
226
+ "cell_type": "code",
227
+ "execution_count": 4,
228
+ "metadata": {},
229
+ "outputs": [
230
+ {
231
+ "data": {
232
+ "text/plain": [
233
+ "[{'type': 'function',\n",
234
+ " 'function': {'name': 'search_person',\n",
235
+ " 'description': 'Search for people in the entertainment industry.',\n",
236
+ " 'parameters': {'type': 'object',\n",
237
+ " 'properties': {'query': {'type': 'string',\n",
238
+ " 'description': 'The search query for the person'},\n",
239
+ " 'include_adult': {'type': 'boolean',\n",
240
+ " 'description': 'Include adult (pornography) content in the results',\n",
241
+ " 'default': False},\n",
242
+ " 'language': {'type': 'string',\n",
243
+ " 'description': 'Language for the search results',\n",
244
+ " 'default': 'en-US'},\n",
245
+ " 'page': {'type': 'integer',\n",
246
+ " 'description': 'Page number of results',\n",
247
+ " 'default': 1}},\n",
248
+ " 'required': ['query']}}},\n",
249
+ " {'type': 'function',\n",
250
+ " 'function': {'name': 'get_person_details',\n",
251
+ " 'description': 'Get detailed information about a specific person.',\n",
252
+ " 'parameters': {'type': 'object',\n",
253
+ " 'properties': {'person_id': {'type': 'integer',\n",
254
+ " 'description': 'The ID of the person to get details for'},\n",
255
+ " 'language': {'type': 'string',\n",
256
+ " 'description': 'Language for the person details',\n",
257
+ " 'default': 'en-US'},\n",
258
+ " 'append_to_response': {'type': 'string',\n",
259
+ " 'description': \"Comma-separated list of additional details to append to the response (e.g., 'images,credits')\"}},\n",
260
+ " 'required': ['person_id']}}}]"
261
+ ]
262
+ },
263
+ "execution_count": 4,
264
+ "metadata": {},
265
+ "output_type": "execute_result"
266
+ }
267
+ ],
268
+ "source": [
269
+ "tools = [\n",
270
+ " {'type': 'function', 'function': {'name': 'search_person'}},\n",
271
+ " {'type': 'function', 'function': {'name': 'get_person_details'}} \n",
272
+ "]\n",
273
+ "\n",
274
+ "messages = [\n",
275
+ " {\"role\": \"system\", \"content\": \"You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer.\"},\n",
276
+ " {\"role\": \"user\", \"content\": \"\"\"Generate a multi-step reasoning chain to answer the query. Include steps for using available tools if necessary.\n",
277
+ " Reasoning chain:\n",
278
+ " \"\"\"},\n",
279
+ " {\"role\": \"assistant\", \"content\": \"Model response\"},\n",
280
+ " ]\n",
281
+ "\n",
282
+ "\n",
283
+ "expected_rendered_text = \"\"\"\n",
284
+ "<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n",
285
+ "\n",
286
+ "Environment: ipython\n",
287
+ "Cutting Knowledge Date: December 2023\n",
288
+ "Today Date: 26 Jul 2024\n",
289
+ "\n",
290
+ "You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer.<|eot_id|>\n",
291
+ "<|start_header_id|>user<|end_header_id|>\n",
292
+ "Generate a multi-step reasoning chain to answer the query. Include steps for using available tools if necessary.\n",
293
+ "<|eot_id|>\n",
294
+ "<|start_header_id|>assistant<|end_header_id|>model_response<|eot_id|>\n",
295
+ "<|start_header_id|>user<|end_header_id|>\n",
296
+ "Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.\n",
297
+ "\n",
298
+ "Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.Do not use variables.\n",
299
+ "\n",
300
+ "{\n",
301
+ " \"type\": \"function\",\n",
302
+ " \"function\": {\n",
303
+ " \"name\": \"discover_movie\"}\n",
304
+ "}\n",
305
+ "\n",
306
+ "{\n",
307
+ " \"type\": \"function\",\n",
308
+ " \"function\": {\n",
309
+ " \"name\": \"get_person_details\"}\n",
310
+ "}\n",
311
+ "\"\"\"\n"
312
+ ]
313
+ },
314
+ {
315
+ "cell_type": "code",
316
+ "execution_count": 4,
317
+ "metadata": {},
318
+ "outputs": [
319
+ {
320
+ "name": "stdout",
321
+ "output_type": "stream",
322
+ "text": [
323
+ "<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n",
324
+ "\n",
325
+ "Environment: ipython\n",
326
+ "Cutting Knowledge Date: December 2023\n",
327
+ "Today Date: 26 Jul 2024\n",
328
+ "\n",
329
+ "You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer.<|eot_id|><|start_header_id|>user<|end_header_id|>\n",
330
+ "\n",
331
+ "Given the following functions, please respond with a JSON for a function call with its proper arguments that best answers the given prompt.\n",
332
+ "\n",
333
+ "Respond in the format {\"name\": function name, \"parameters\": dictionary of argument name and its value}.Do not use variables.\n",
334
+ "\n",
335
+ "{\n",
336
+ " \"type\": \"function\",\n",
337
+ " \"function\": {\n",
338
+ " \"name\": \"discover_movie\",\n",
339
+ " \"description\": \"Find movies using over 30 filters and sort options\",\n",
340
+ " \"parameters\": {\n",
341
+ " \"type\": \"object\",\n",
342
+ " \"properties\": {\n",
343
+ " \"region\": {\n",
344
+ " \"type\": \"string\",\n",
345
+ " \"description\": \"ISO 3166-1 code to filter release dates\"\n",
346
+ " },\n",
347
+ " \"sort_by\": {\n",
348
+ " \"type\": \"string\",\n",
349
+ " \"description\": \"Sort the results\"\n",
350
+ " },\n",
351
+ " \"release_date.gte\": {\n",
352
+ " \"type\": \"string\",\n",
353
+ " \"description\": \"Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value\"\n",
354
+ " },\n",
355
+ " \"release_date.lte\": {\n",
356
+ " \"type\": \"string\",\n",
357
+ " \"description\": \"Filter and only include movies that have a release date (looking at all release dates) that is less than or equal to the specified value\"\n",
358
+ " },\n",
359
+ " \"with_release_type\": {\n",
360
+ " \"type\": \"integer\",\n",
361
+ " \"description\": \"Specify a comma (AND) or pipe (OR) separated value to filter release types\"\n",
362
+ " },\n",
363
+ " \"year\": {\n",
364
+ " \"type\": \"integer\",\n",
365
+ " \"description\": \"Filter the results to only include movies that have a release year that equals the specified value\"\n",
366
+ " },\n",
367
+ " \"with_cast\": {\n",
368
+ " \"type\": \"string\",\n",
369
+ " \"description\": \"A comma separated list of person ID's to filter the results with\"\n",
370
+ " },\n",
371
+ " \"with_crew\": {\n",
372
+ " \"type\": \"string\",\n",
373
+ " \"description\": \"A comma separated list of person ID's to filter the results with\"\n",
374
+ " },\n",
375
+ " \"with_people\": {\n",
376
+ " \"type\": \"string\",\n",
377
+ " \"description\": \"A comma separated list of person ID's to filter the results with\"\n",
378
+ " },\n",
379
+ " \"with_companies\": {\n",
380
+ " \"type\": \"string\",\n",
381
+ " \"description\": \"A comma separated list of production company ID's to filter the results with\"\n",
382
+ " },\n",
383
+ " \"with_genres\": {\n",
384
+ " \"type\": \"string\",\n",
385
+ " \"description\": \"A comma separated list of genre ID's to filter the results with\"\n",
386
+ " },\n",
387
+ " \"without_genres\": {\n",
388
+ " \"type\": \"string\",\n",
389
+ " \"description\": \"A comma separated list of genre ID's to exclude from the results\"\n",
390
+ " },\n",
391
+ " \"with_keywords\": {\n",
392
+ " \"type\": \"string\",\n",
393
+ " \"description\": \"A comma separated list of keyword ID's to filter the results with\"\n",
394
+ " },\n",
395
+ " \"without_keywords\": {\n",
396
+ " \"type\": \"string\",\n",
397
+ " \"description\": \"A comma separated list of keyword ID's to exclude from the results\"\n",
398
+ " }\n",
399
+ " },\n",
400
+ " \"required\": []\n",
401
+ " }\n",
402
+ " }\n",
403
+ "}\n",
404
+ "\n",
405
+ "{\n",
406
+ " \"type\": \"function\",\n",
407
+ " \"function\": {\n",
408
+ " \"name\": \"get_movie_details\",\n",
409
+ " \"description\": \"Get the top level details of a movie by ID\",\n",
410
+ " \"parameters\": {\n",
411
+ " \"type\": \"object\",\n",
412
+ " \"properties\": {\n",
413
+ " \"movie_id\": {\n",
414
+ " \"type\": \"integer\",\n",
415
+ " \"description\": \"The ID of the movie to get details for\"\n",
416
+ " },\n",
417
+ " \"append_to_response\": {\n",
418
+ " \"type\": \"string\",\n",
419
+ " \"description\": \"Comma-separated list of sub requests to append to the response\"\n",
420
+ " }\n",
421
+ " },\n",
422
+ " \"required\": [\n",
423
+ " \"movie_id\"\n",
424
+ " ]\n",
425
+ " }\n",
426
+ " }\n",
427
+ "}\n",
428
+ "\n",
429
+ "{\n",
430
+ " \"type\": \"function\",\n",
431
+ " \"function\": {\n",
432
+ " \"name\": \"search_person\",\n",
433
+ " \"description\": \"Search for people in the entertainment industry.\",\n",
434
+ " \"parameters\": {\n",
435
+ " \"type\": \"object\",\n",
436
+ " \"properties\": {\n",
437
+ " \"query\": {\n",
438
+ " \"type\": \"string\",\n",
439
+ " \"description\": \"The search query for the person\"\n",
440
+ " },\n",
441
+ " \"include_adult\": {\n",
442
+ " \"type\": \"boolean\",\n",
443
+ " \"description\": \"Include adult (pornography) content in the results\",\n",
444
+ " \"default\": false\n",
445
+ " },\n",
446
+ " \"language\": {\n",
447
+ " \"type\": \"string\",\n",
448
+ " \"description\": \"Language for the search results\",\n",
449
+ " \"default\": \"en-US\"\n",
450
+ " },\n",
451
+ " \"page\": {\n",
452
+ " \"type\": \"integer\",\n",
453
+ " \"description\": \"Page number of results\",\n",
454
+ " \"default\": 1\n",
455
+ " }\n",
456
+ " },\n",
457
+ " \"required\": [\n",
458
+ " \"query\"\n",
459
+ " ]\n",
460
+ " }\n",
461
+ " }\n",
462
+ "}\n",
463
+ "\n",
464
+ "{\n",
465
+ " \"type\": \"function\",\n",
466
+ " \"function\": {\n",
467
+ " \"name\": \"get_person_details\",\n",
468
+ " \"description\": \"Get detailed information about a specific person.\",\n",
469
+ " \"parameters\": {\n",
470
+ " \"type\": \"object\",\n",
471
+ " \"properties\": {\n",
472
+ " \"person_id\": {\n",
473
+ " \"type\": \"integer\",\n",
474
+ " \"description\": \"The ID of the person to get details for\"\n",
475
+ " },\n",
476
+ " \"language\": {\n",
477
+ " \"type\": \"string\",\n",
478
+ " \"description\": \"Language for the person details\",\n",
479
+ " \"default\": \"en-US\"\n",
480
+ " },\n",
481
+ " \"append_to_response\": {\n",
482
+ " \"type\": \"string\",\n",
483
+ " \"description\": \"Comma-separated list of additional details to append to the response (e.g., 'images,credits')\"\n",
484
+ " }\n",
485
+ " },\n",
486
+ " \"required\": [\n",
487
+ " \"person_id\"\n",
488
+ " ]\n",
489
+ " }\n",
490
+ " }\n",
491
+ "}\n",
492
+ "\n",
493
+ "{\n",
494
+ " \"type\": \"function\",\n",
495
+ " \"function\": {\n",
496
+ " \"name\": \"get_movie_genres\",\n",
497
+ " \"description\": \"Get the list of official genres for movies.\",\n",
498
+ " \"parameters\": {\n",
499
+ " \"type\": \"object\",\n",
500
+ " \"properties\": {\n",
501
+ " \"language\": {\n",
502
+ " \"type\": \"string\",\n",
503
+ " \"description\": \"Language for the genre names\",\n",
504
+ " \"default\": \"en-US\"\n",
505
+ " }\n",
506
+ " }\n",
507
+ " }\n",
508
+ " }\n",
509
+ "}\n",
510
+ "\n",
511
+ "Given the user query: \"What are the genres of the movie 'The Dark Knight'?\"\n",
512
+ " Generate a multi-step reasoning chain to answer the query. Include steps for using available tools if necessary.<|eot_id|>\n"
513
+ ]
514
+ }
515
+ ],
516
+ "source": [
517
+ "import textwrap\n",
518
+ "\n",
519
+ "\n",
520
+ "query = \"What are the genres of the movie 'The Dark Knight'?\"\n",
521
+ "\n",
522
+ "user_message = f\"\"\"\n",
523
+ " Given the user query: \"{query}\"\n",
524
+ " Generate a multi-step reasoning chain to answer the query. Include steps for using available tools if necessary.\n",
525
+ " \"\"\"\n",
526
+ "messages = [\n",
527
+ " {\"role\": \"system\", \"content\": \"You are a movie search assistant bot who uses TMDB to help users find movies. Think step by step and identify the sequence of function calls that will help to answer.\"},\n",
528
+ " {\"role\": \"user\", \"content\": user_message},\n",
529
+ " ]\n",
530
+ "\n",
531
+ "chat = tokenizer.apply_chat_template(\n",
532
+ " messages, tools=tools, \n",
533
+ " add_generation_prompt=False, \n",
534
+ " tools_in_user_message=True,\n",
535
+ " tokenize=False, \n",
536
+ " return_tensors=\"pt\")\n",
537
+ "print(chat)\n"
538
+ ]
539
+ },
540
+ {
541
+ "cell_type": "code",
542
+ "execution_count": null,
543
+ "metadata": {},
544
+ "outputs": [],
545
+ "source": [
546
+ "response = generate_reasoning_chain(\"What are the genres of the movie 'The Dark Knight'?\")\n",
547
+ "print(response)"
548
+ ]
549
+ },
550
+ {
551
+ "cell_type": "code",
552
+ "execution_count": null,
553
+ "metadata": {},
554
+ "outputs": [],
555
+ "source": [
556
+ "\"\"\"\n",
557
+ "<|begin_of_text|>\n",
558
+ "<|start_header_id|>system<|end_header_id|> {{ system_prompt }}<|eot_id|>\n",
559
+ "<|start_header_id|>user<|end_header_id|> {{ user_message_1 }}<|eot_id|>\n",
560
+ "<|start_header_id|>assistant<|end_header_id|> <|python_tag|>{{ model_tool_call_1 }}<|eom_id|>\n",
561
+ "<|start_header_id|>ipython<|end_header_id|> {{ tool_response }}<|eot_id|>\n",
562
+ "<|start_header_id|>assistant<|end_header_id|> {{ model_response_based_on_tool_response }}<|eot_id|>\n",
563
+ "\"\"\"\n",
564
+ "print(response)"
565
+ ]
566
+ }
567
+ ],
568
+ "metadata": {
569
+ "kernelspec": {
570
+ "display_name": "movies-app",
571
+ "language": "python",
572
+ "name": "python3"
573
+ },
574
+ "language_info": {
575
+ "codemirror_mode": {
576
+ "name": "ipython",
577
+ "version": 3
578
+ },
579
+ "file_extension": ".py",
580
+ "mimetype": "text/x-python",
581
+ "name": "python",
582
+ "nbconvert_exporter": "python",
583
+ "pygments_lexer": "ipython3",
584
+ "version": "3.11.9"
585
+ }
586
+ },
587
+ "nbformat": 4,
588
+ "nbformat_minor": 2
589
+ }
tools.py CHANGED
@@ -7,14 +7,6 @@ tools = [
7
  "parameters": {
8
  "type": "object",
9
  "properties": {
10
- "region": {
11
- "type": "string",
12
- "description": "ISO 3166-1 code to filter release dates",
13
- },
14
- "sort_by": {
15
- "type": "string",
16
- "description": "Sort the results",
17
- },
18
  "release_date.gte": {
19
  "type": "string",
20
  "description": "Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value",
@@ -33,35 +25,11 @@ tools = [
33
  },
34
  "with_cast": {
35
  "type": "string",
36
- "description": "A comma separated list of person ID's to filter the results with",
37
- },
38
- "with_crew": {
39
- "type": "string",
40
- "description": "A comma separated list of person ID's to filter the results with",
41
- },
42
- "with_people": {
43
- "type": "string",
44
- "description": "A comma separated list of person ID's to filter the results with",
45
- },
46
- "with_companies": {
47
- "type": "string",
48
- "description": "A comma separated list of production company ID's to filter the results with",
49
  },
50
  "with_genres": {
51
  "type": "string",
52
- "description": "A comma separated list of genre ID's to filter the results with",
53
- },
54
- "without_genres": {
55
- "type": "string",
56
- "description": "A comma separated list of genre ID's to exclude from the results",
57
- },
58
- "with_keywords": {
59
- "type": "string",
60
- "description": "A comma separated list of keyword ID's to filter the results with",
61
- },
62
- "without_keywords": {
63
- "type": "string",
64
- "description": "A comma separated list of keyword ID's to exclude from the results",
65
  },
66
  },
67
  "required": [],
@@ -78,11 +46,7 @@ tools = [
78
  "properties": {
79
  "movie_id": {
80
  "type": "integer",
81
- "description": "The ID of the movie to get details for",
82
- },
83
- "append_to_response": {
84
- "type": "string",
85
- "description": "Comma-separated list of sub requests to append to the response",
86
  },
87
  },
88
  "required": ["movie_id"],
@@ -101,21 +65,6 @@ tools = [
101
  "type": "string",
102
  "description": "The search query for the person"
103
  },
104
- "include_adult": {
105
- "type": "boolean",
106
- "description": "Include adult (pornography) content in the results",
107
- "default": False
108
- },
109
- "language": {
110
- "type": "string",
111
- "description": "Language for the search results",
112
- "default": "en-US"
113
- },
114
- "page": {
115
- "type": "integer",
116
- "description": "Page number of results",
117
- "default": 1
118
- }
119
  },
120
  "required": ["query"]
121
  }
@@ -131,17 +80,8 @@ tools = [
131
  "properties": {
132
  "person_id": {
133
  "type": "integer",
134
- "description": "The ID of the person to get details for"
135
  },
136
- "language": {
137
- "type": "string",
138
- "description": "Language for the person details",
139
- "default": "en-US"
140
- },
141
- "append_to_response": {
142
- "type": "string",
143
- "description": "Comma-separated list of additional details to append to the response (e.g., 'images,credits')"
144
- }
145
  },
146
  "required": ["person_id"]
147
  }
@@ -152,16 +92,6 @@ tools = [
152
  "function": {
153
  "name": "get_movie_genres",
154
  "description": "Get the list of official genres for movies.",
155
- "parameters": {
156
- "type": "object",
157
- "properties": {
158
- "language": {
159
- "type": "string",
160
- "description": "Language for the genre names",
161
- "default": "en-US"
162
- }
163
- }
164
- }
165
  }
166
  }
167
  ]
 
7
  "parameters": {
8
  "type": "object",
9
  "properties": {
 
 
 
 
 
 
 
 
10
  "release_date.gte": {
11
  "type": "string",
12
  "description": "Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value",
 
25
  },
26
  "with_cast": {
27
  "type": "string",
28
+ "description": "A comma separated list of person ID's to filter the results with. Use seach_person function to find the ID of a person.",
 
 
 
 
 
 
 
 
 
 
 
 
29
  },
30
  "with_genres": {
31
  "type": "string",
32
+ "description": "A comma separated list of genre ID's to filter the results with. Use get_movie_genres function to find the ID of a genre.",
 
 
 
 
 
 
 
 
 
 
 
 
33
  },
34
  },
35
  "required": [],
 
46
  "properties": {
47
  "movie_id": {
48
  "type": "integer",
49
+ "description": "The ID of the movie to get details for. Use discover_movie to find the ID of a movie.",
 
 
 
 
50
  },
51
  },
52
  "required": ["movie_id"],
 
65
  "type": "string",
66
  "description": "The search query for the person"
67
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  },
69
  "required": ["query"]
70
  }
 
80
  "properties": {
81
  "person_id": {
82
  "type": "integer",
83
+ "description": "The ID of the person to get details for. Use search_person to find the ID of a person."
84
  },
 
 
 
 
 
 
 
 
 
85
  },
86
  "required": ["person_id"]
87
  }
 
92
  "function": {
93
  "name": "get_movie_genres",
94
  "description": "Get the list of official genres for movies.",
 
 
 
 
 
 
 
 
 
 
95
  }
96
  }
97
  ]