tmzh commited on
Commit
b6d6046
2 Parent(s): 235762c 03c066e

Merge branch 'master' into main

Browse files
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ __pycache__/
2
+ .env
3
+
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.8-slim-buster
3
+
4
+ # Set the working directory in the container to /app
5
+ WORKDIR /app
6
+
7
+ # Copy the current directory contents into the container at /app
8
+ ADD . /app
9
+
10
+ # Install any needed packages specified in requirements.txt
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # Make port 7860 available to the world outside this container
14
+ EXPOSE 7860
15
+
16
+ # Define environment variable
17
+ ENV NAME World
18
+
19
+ # Run app.py when the container launches
20
+ CMD ["python", "app.py"]
agent.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.")
app.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ from agent import chatbot
3
+ from functions import query_tmdb
4
+
5
+ app = Flask(__name__)
6
+
7
+
8
+ @app.route('/')
9
+ def index():
10
+ page = request.args.get('page', 1)
11
+ url = f'https://api.themoviedb.org/3/movie/popular?page={page}'
12
+ data = query_tmdb(url)
13
+ movies = data.get('results', [])
14
+ total_pages = data.get('total_pages', 1)
15
+ return render_template('index.html', movies=movies, page=int(page), total_pages=total_pages, title="Popular Movies")
16
+
17
+
18
+ @app.route('/movie/<int:movie_id>')
19
+ def movie_details(movie_id):
20
+ url = f'https://api.themoviedb.org/3/movie/{movie_id}'
21
+ movie = query_tmdb(url)
22
+ return jsonify(movie)
23
+
24
+
25
+ @app.route('/movie/<int:movie_id>/credits')
26
+ def movie_credits(movie_id):
27
+ url = f'https://api.themoviedb.org/3/movie/{movie_id}/credits'
28
+ credits = query_tmdb(url)
29
+ return jsonify(credits)
30
+
31
+
32
+ @app.route('/genre/<int:genre_id>')
33
+ def genre_filter(genre_id):
34
+ page = request.args.get('page', 1)
35
+ url = f'https://api.themoviedb.org/3/discover/movie?with_genres={genre_id}&page={page}'
36
+ data = query_tmdb(url)
37
+ genre_url = 'https://api.themoviedb.org/3/genre/movie/list'
38
+ genres = query_tmdb(genre_url).get('genres', [])
39
+ genre_name = next(
40
+ (genre['name'] for genre in genres if genre['id'] == genre_id), 'Unknown')
41
+ return render_template('index.html', movies=data.get('results', []), page=int(page), total_pages=data.get('total_pages', 1), title=f"Movies in Genre: {genre_name}")
42
+
43
+
44
+ @app.route('/genres')
45
+ def genres():
46
+ genre_url = 'https://api.themoviedb.org/3/genre/movie/list'
47
+ genres = query_tmdb(genre_url).get('genres', [])
48
+ return render_template('genres.html', genres=genres)
49
+
50
+
51
+ @app.route('/cast')
52
+ def cast():
53
+ page = request.args.get('page', 1)
54
+ url = f'https://api.themoviedb.org/3/trending/person/week?page={page}'
55
+ data = query_tmdb(url)
56
+ persons = data.get('results', [])
57
+ total_pages = data.get('total_pages', 1)
58
+ return render_template('cast.html', persons=persons, page=int(page), total_pages=total_pages, title="Trending Cast Members")
59
+
60
+
61
+ class PersonInfo:
62
+ def __init__(self, name, image, bio):
63
+ self.name = name
64
+ self.image = image
65
+ self.bio = bio
66
+
67
+
68
+ @app.route('/cast/<int:cast_id>')
69
+ def cast_filter(cast_id):
70
+ page = request.args.get('page', 1)
71
+ url = f'https://api.themoviedb.org/3/discover/movie?with_cast={cast_id}&page={page}'
72
+ data = query_tmdb(url)
73
+ cast_url = f'https://api.themoviedb.org/3/person/{cast_id}'
74
+ cast_data = query_tmdb(cast_url)
75
+ cast_name = cast_data.get('name', 'Unknown')
76
+ cast_bio = cast_data.get('biography', 'No biography available')
77
+ cast_image = f"https://image.tmdb.org/t/p/w500{cast_data.get('profile_path', '')}"
78
+ cast_info = PersonInfo(name=cast_name, image=cast_image, bio=cast_bio)
79
+ return render_template('index.html',
80
+ movies=data.get('results', []),
81
+ page=int(page),
82
+ total_pages=data.get('total_pages', 1),
83
+ title=f"Movies with Actor/Director: {cast_name}",
84
+ cast_info=cast_info)
85
+
86
+
87
+ @app.route('/query', methods=['GET', 'POST'])
88
+ def query():
89
+ if request.method == 'POST':
90
+ query = request.form.get('query')
91
+ result = chatbot(query) # Call the chatbot function from agent.py
92
+ print("Got result: ", result)
93
+ print("Movie ids are: ", [r['id'] for r in result])
94
+ return render_template('query.html', result=result)
95
+ return render_template('query.html')
96
+
97
+
98
+ if __name__ == '__main__':
99
+ app.run(host='0.0.0.0', port=5000)
functions.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+
4
+ BASE_URL = "https://api.themoviedb.org/3"
5
+ API_KEY = os.environ['TMDB_API_KEY']
6
+
7
+
8
+ def query_tmdb(endpoint, params={}):
9
+ headers = {
10
+ "accept": "application/json",
11
+ "Authorization": f"Bearer {API_KEY}"
12
+ }
13
+ response = requests.get(endpoint, headers=headers, params=params)
14
+ return response.json()
15
+
16
+
17
+ def discover_movie(include_adult=False, include_video=False, language="en-US", page=1, sort_by="popularity.desc", **kwargs):
18
+ endpoint = f"{BASE_URL}/discover/movie"
19
+ params = {}
20
+
21
+ for key, value in kwargs.items():
22
+ if value is not None:
23
+ params[key] = value
24
+ response = query_tmdb(endpoint, params=params)
25
+ return response
26
+
27
+
28
+ def get_movie_details(movie_id, append_to_response=None):
29
+ endpoint = f"{BASE_URL}/movie/{movie_id}"
30
+ params = {}
31
+
32
+ response = query_tmdb(endpoint, params=params)
33
+ return response
34
+
35
+
36
+ def search_person(query, include_adult=False, language="en-US", page=1):
37
+ endpoint = f"{BASE_URL}/search/person"
38
+ params = {
39
+ 'query': query,
40
+ 'include_adult': include_adult,
41
+ 'language': language,
42
+ 'page': page
43
+ }
44
+
45
+ response = query_tmdb(endpoint, params=params)
46
+ return response
47
+
48
+
49
+ def get_person_details(person_id, language="en-US", append_to_response=None):
50
+ endpoint = f"{BASE_URL}/person/{person_id}"
51
+ params = {
52
+ 'language': language
53
+ }
54
+ if append_to_response:
55
+ params['append_to_response'] = append_to_response
56
+
57
+ response = query_tmdb(endpoint, params=params)
58
+ return response
59
+
60
+
61
+ def get_movie_genres(language="en-US"):
62
+ endpoint = f"{BASE_URL}/genre/movie/list"
63
+ params = {
64
+ 'language': language
65
+ }
66
+
67
+ response = query_tmdb(endpoint, params=params)
68
+ return response
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Flask
2
+ requests
3
+ transformers
4
+ torch
5
+ bitsandbytes
6
+ accelerate
static/css/style.css ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #sidebar {
2
+ width: 200px;
3
+ position: fixed;
4
+ height: 100%;
5
+ left: 0;
6
+ background-color: #f8f9fa;
7
+ padding: 20px;
8
+ }
9
+
10
+ #sidebar ul {
11
+ list-style-type: none;
12
+ padding: 0;
13
+ }
14
+
15
+ #sidebar li {
16
+ margin-bottom: 10px;
17
+ }
18
+
19
+ #sidebar a {
20
+ text-decoration: none;
21
+ color: #333;
22
+ }
23
+
24
+ #main-content {
25
+ margin-left: 200px;
26
+ padding: 40px;
27
+ }
28
+
29
+ .tile-element {
30
+ display: inline-block;
31
+ width: 200px;
32
+ margin: 10px;
33
+ cursor: pointer;
34
+ vertical-align: top;
35
+ }
36
+
37
+ .tile-element img {
38
+ width: 100%;
39
+ }
40
+
41
+ #side-panel {
42
+ position: fixed;
43
+ top: 50%;
44
+ left: 50%;
45
+ transform: translate(-50%, -50%);
46
+ width: 60%;
47
+ background: white;
48
+ border: 1px solid #ccc;
49
+ padding: 20px;
50
+ z-index: 1000;
51
+ display: none;
52
+ flex-direction: row;
53
+ justify-content: space-between;
54
+ align-items: flex-start;
55
+ }
56
+
57
+ #side-panel.active {
58
+ display: flex;
59
+ }
60
+
61
+ #close-button {
62
+ align-self: flex-start;
63
+ cursor: pointer;
64
+ font-size: 20px;
65
+ }
66
+
67
+ #movie-details {
68
+ padding-left: 20px;
69
+ text-align: left;
70
+ flex-direction: column;
71
+ }
72
+
73
+ #movie-poster {
74
+ width: 30%;
75
+ object-fit: contain;
76
+ }
77
+
78
+ #overlay {
79
+ display: none;
80
+ /* Hidden by default */
81
+ position: fixed;
82
+ top: 0;
83
+ left: 0;
84
+ width: 100%;
85
+ height: 100%;
86
+ background: rgba(0, 0, 0, 0.5);
87
+ /* Semi-transparent background */
88
+ z-index: 999;
89
+ }
90
+
91
+ #overlay.active {
92
+ display: block;
93
+ /* Show overlay when active */
94
+ }
static/js/script.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ function generatePanelContent(data) {
3
+ const { movie, credits } = data;
4
+
5
+ const posterUrl = movie.poster_path
6
+ ? `https://image.tmdb.org/t/p/w200${movie.poster_path}`
7
+ : 'default_image_url';
8
+
9
+ const genres = movie.genres.map(genre => {
10
+ return `<a href="/genre/${genre.id}">${genre.name}</a>`;
11
+ }).join(', ');
12
+
13
+ const director = credits.crew.find(crewMember => crewMember.job === 'Director');
14
+ const directorLink = director
15
+ ? `<a href="/cast/${director.id}">${director.name}</a>`
16
+ : 'N/A';
17
+
18
+ const cast = credits.cast.slice(0, 5).map(castMember => {
19
+ return `<a href="/cast/${castMember.id}">${castMember.name}</a>`;
20
+ }).join(', ');
21
+
22
+ const movieDetailsInnerHTML = `
23
+ <h2>${movie.title}</h2>
24
+ <p>${movie.overview}</p>
25
+ <strong>Genres:</strong> ${genres}<br>
26
+ <strong>Director:</strong> ${directorLink}<br>
27
+ <strong>Cast:</strong> ${cast}`;
28
+
29
+ return { posterUrl, movieDetailsInnerHTML };
30
+ }
31
+
32
+ function showDetails(movieId) {
33
+ fetch(`/movie/${movieId}`)
34
+ .then(response => response.json())
35
+ .then(movie => {
36
+ fetch(`/movie/${movieId}/credits`)
37
+ .then(response => response.json())
38
+ .then(credits => {
39
+ const panel = document.getElementById('side-panel');
40
+ const overlay = document.getElementById('overlay');
41
+ const moviePoster = document.getElementById('movie-poster');
42
+ const movieDetails = document.getElementById('movie-details');
43
+
44
+ // Retrieve posterUrl and movieDetailsInnerHTML from generatePanelContent
45
+ const { posterUrl, movieDetailsInnerHTML } = generatePanelContent({ movie, credits });
46
+
47
+ // Update the respective DOM elements
48
+ moviePoster.src = posterUrl;
49
+ movieDetails.innerHTML = movieDetailsInnerHTML;
50
+
51
+ panel.classList.add('active'); // Add active class to slide in
52
+ overlay.classList.add('active'); // Show overlay
53
+ });
54
+ });
55
+ }
56
+
57
+ function closePanel() {
58
+ const panel = document.getElementById('side-panel');
59
+ const overlay = document.getElementById('overlay');
60
+ panel.classList.remove('active'); // Remove active class to slide out
61
+ overlay.classList.remove('active'); // Hide overlay
62
+ }
templates/base.html ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>TMDB Movies Explorer</title>
8
+
9
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
10
+ <script src="{{ url_for('static', filename='js/script.js') }}"></script>
11
+ </head>
12
+
13
+ <body>
14
+ <div id="sidebar">
15
+ <ul>
16
+ <li><a href="/">Popular Movies</a></li>
17
+ <li><a href="/genres">By Genre</a></li>
18
+ <li><a href="/cast">By Cast</a></li>
19
+ <li><a href="/query">Query</a></li>
20
+ </ul>
21
+ </div>
22
+ <div id="main-content">
23
+ {% block content %}{% endblock %}
24
+ </div>
25
+ </body>
26
+
27
+ </html>
templates/cast.html ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+
3
+ {% block content %}
4
+ {% for person in persons %}
5
+ <div class="tile-element">
6
+ <a href="cast/{{ person.id }}">
7
+ <img src="https://image.tmdb.org/t/p/w500{{ person.profile_path }}" alt="{{ person.name}}">
8
+ </a>
9
+ <h2>{{ person.name }}</h2>
10
+ </div>
11
+ {% endfor %}
12
+ {% endblock %}
templates/genres.html ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- genres.html -->
2
+ {% extends 'base.html' %}
3
+
4
+ {% block content %}
5
+ <h1>Genres</h1>
6
+ <ul>
7
+ {% for genre in genres %}
8
+ <li><a href="/genre/{{ genre.id }}">{{ genre.name }}</a></li>
9
+ {% endfor %}
10
+ </ul>
11
+ {% endblock %}
templates/index.html ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends 'base.html' %}
2
+
3
+ {% block content %}
4
+
5
+ {% if cast_info %}
6
+ <div style="display: flex;">
7
+ <div style="max-width: 30%;">
8
+ <img src="{{ cast_info.image }}" alt="{{ title }}" style="width: 300px; object-fit: contain;">
9
+ </div>
10
+ <div style="max-width: 70%; padding-left: 20px;">
11
+ <h2>{{ cast_info.name }}</h2>
12
+ <p>{{ cast_info.bio }}</p>
13
+ </div>
14
+ </div>
15
+ <h2>{{ title }}</h2>
16
+ {% else %}
17
+ <h1>{{ title }}</h1>
18
+ {% endif %}
19
+
20
+ <div>
21
+ {% for movie in movies %}
22
+ <div class="tile-element" onclick="showDetails({{ movie.id }})">
23
+ <img src="https://image.tmdb.org/t/p/w500{{ movie.poster_path }}" alt="{{ movie.title }}">
24
+ <h4>{{ movie.title }}</h4>
25
+ </div>
26
+ {% endfor %}
27
+ </div>
28
+ <div>
29
+ {% if page > 1 %}
30
+ <a href="?page={{ page - 1 }}">Previous</a>
31
+ {% endif %}
32
+ {% if page < total_pages %} <a href="?page={{ page + 1 }}">Next</a>
33
+ {% endif %}
34
+ </div>
35
+
36
+ <div id="overlay" onclick="closePanel()"></div> <!-- Overlay for closing panel -->
37
+ <div id="side-panel">
38
+ <img id="movie-poster" src="" alt="${movie.title} Poster" style="max-width: 150px;">
39
+ <div id="movie-details">
40
+ <!-- Movie details will be injected here -->
41
+ </div>
42
+ <span id="close-button" onclick="closePanel()">&times;</span>
43
+
44
+ </div>
45
+ {% endblock %}
templates/query.html ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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...">
7
+ <input type="submit" value="Submit">
8
+ </form>
9
+
10
+ {% if result %}
11
+ <h2>Query Result</h2>
12
+ <div>
13
+ {% for movie in result %}
14
+ <div class="tile-element" onclick="showDetails({{ movie.id }})">
15
+ <img src="https://image.tmdb.org/t/p/w500{{ movie.poster_path }}" alt="{{ movie.title }}">
16
+ <h4>{{ movie.title }}</h4>
17
+ </div>
18
+ {% endfor %}
19
+ </div>
20
+ {% elif result is defined %}
21
+ <p>No results found.</p>
22
+ {% endif %}
23
+ {% endblock %}
tools.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ tools = [
2
+ {
3
+ "type": "function",
4
+ "function": {
5
+ "name": "discover_movie",
6
+ "description": "Find movies using over 30 filters and sort options",
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
+ "certification_country": {
19
+ "type": "string",
20
+ "description": "Used in conjunction with the certification filter",
21
+ },
22
+ "certification": {
23
+ "type": "string",
24
+ "description": "Filter results with a valid certification from the certification_country",
25
+ },
26
+ "certification.lte": {
27
+ "type": "string",
28
+ "description": "Filter and only include movies that have a certification that is less than or equal to the specified value",
29
+ },
30
+ "certification.gte": {
31
+ "type": "string",
32
+ "description": "Filter and only include movies that have a certification that is greater than or equal to the specified value",
33
+ },
34
+ "include_adult": {
35
+ "type": "boolean",
36
+ "description": "Choose whether to include adult (pornography) content in the results",
37
+ },
38
+ "include_video": {
39
+ "type": "boolean",
40
+ "description": "Choose whether to include videos in the results",
41
+ },
42
+ "page": {
43
+ "type": "integer",
44
+ "description": "Specify which page to query",
45
+ },
46
+ "primary_release_year": {
47
+ "type": "integer",
48
+ "description": "Filter the results to only include movies that have a primary release year that equals the specified value",
49
+ },
50
+ "primary_release_date.gte": {
51
+ "type": "string",
52
+ "description": "Filter and only include movies that have a primary release date that is greater or equal to the specified value",
53
+ },
54
+ "primary_release_date.lte": {
55
+ "type": "string",
56
+ "description": "Filter and only include movies that have a primary release date that is less than or equal to the specified value",
57
+ },
58
+ "release_date.gte": {
59
+ "type": "string",
60
+ "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",
61
+ },
62
+ "release_date.lte": {
63
+ "type": "string",
64
+ "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",
65
+ },
66
+ "with_release_type": {
67
+ "type": "integer",
68
+ "description": "Specify a comma (AND) or pipe (OR) separated value to filter release types",
69
+ },
70
+ "year": {
71
+ "type": "integer",
72
+ "description": "Filter the results to only include movies that have a release year that equals the specified value",
73
+ },
74
+ "vote_count.gte": {
75
+ "type": "integer",
76
+ "description": "Filter and only include movies that have a vote count that is greater or equal to the specified value",
77
+ },
78
+ "vote_count.lte": {
79
+ "type": "integer",
80
+ "description": "Filter and only include movies that have a vote count that is less than or equal to the specified value",
81
+ },
82
+ "vote_average.gte": {
83
+ "type": "number",
84
+ "description": "Filter and only include movies that have a rating that is greater or equal to the specified value",
85
+ },
86
+ "vote_average.lte": {
87
+ "type": "number",
88
+ "description": "Filter and only include movies that have a rating that is less than or equal to the specified value",
89
+ },
90
+ "with_cast": {
91
+ "type": "string",
92
+ "description": "A comma separated list of person ID's to filter the results with",
93
+ },
94
+ "with_crew": {
95
+ "type": "string",
96
+ "description": "A comma separated list of person ID's to filter the results with",
97
+ },
98
+ "with_people": {
99
+ "type": "string",
100
+ "description": "A comma separated list of person ID's to filter the results with",
101
+ },
102
+ "with_companies": {
103
+ "type": "string",
104
+ "description": "A comma separated list of production company ID's to filter the results with",
105
+ },
106
+ "with_genres": {
107
+ "type": "string",
108
+ "description": "A comma separated list of genre ID's to filter the results with",
109
+ },
110
+ "without_genres": {
111
+ "type": "string",
112
+ "description": "A comma separated list of genre ID's to exclude from the results",
113
+ },
114
+ "with_keywords": {
115
+ "type": "string",
116
+ "description": "A comma separated list of keyword ID's to filter the results with",
117
+ },
118
+ "without_keywords": {
119
+ "type": "string",
120
+ "description": "A comma separated list of keyword ID's to exclude from the results",
121
+ },
122
+ "with_runtime.gte": {
123
+ "type": "integer",
124
+ "description": "Filter and only include movies that have a runtime that is greater or equal to the specified value",
125
+ },
126
+ "with_runtime.lte": {
127
+ "type": "integer",
128
+ "description": "Filter and only include movies that have a runtime that is less than or equal to the specified value",
129
+ },
130
+ "with_original_language": {
131
+ "type": "string",
132
+ "description": "Specify an ISO 639-1 string to filter results by their original language value",
133
+ },
134
+ "with_watch_providers": {
135
+ "type": "string",
136
+ "description": "A comma or pipe separated list of watch provider ID's to filter the results with",
137
+ },
138
+ "watch_region": {
139
+ "type": "string",
140
+ "description": "An ISO 3166-1 code to filter the watch provider results",
141
+ },
142
+ "with_watch_monetization_types": {
143
+ "type": "string",
144
+ "description": "Filter the results by monetization type",
145
+ },
146
+ "without_companies": {
147
+ "type": "string",
148
+ "description": "A comma separated list of production company ID's to filter the results with",
149
+ },
150
+ },
151
+ "required": [],
152
+ },
153
+ },
154
+ },
155
+ {
156
+ "type": "function",
157
+ "function": {
158
+ "name": "get_movie_details",
159
+ "description": "Get the top level details of a movie by ID",
160
+ "parameters": {
161
+ "type": "object",
162
+ "properties": {
163
+ "movie_id": {
164
+ "type": "integer",
165
+ "description": "The ID of the movie to get details for",
166
+ },
167
+ "append_to_response": {
168
+ "type": "string",
169
+ "description": "Comma-separated list of sub requests to append to the response",
170
+ },
171
+ },
172
+ "required": ["movie_id"],
173
+ },
174
+ },
175
+ },
176
+ {
177
+ "type": "function",
178
+ "function": {
179
+ "name": "search_person",
180
+ "description": "Search for people in the entertainment industry.",
181
+ "parameters": {
182
+ "type": "object",
183
+ "properties": {
184
+ "query": {
185
+ "type": "string",
186
+ "description": "The search query for the person"
187
+ },
188
+ "include_adult": {
189
+ "type": "boolean",
190
+ "description": "Include adult (pornography) content in the results",
191
+ "default": False
192
+ },
193
+ "language": {
194
+ "type": "string",
195
+ "description": "Language for the search results",
196
+ "default": "en-US"
197
+ },
198
+ "page": {
199
+ "type": "integer",
200
+ "description": "Page number of results",
201
+ "default": 1
202
+ }
203
+ },
204
+ "required": ["query"]
205
+ }
206
+ }
207
+ },
208
+ {
209
+ "type": "function",
210
+ "function": {
211
+ "name": "get_person_details",
212
+ "description": "Get detailed information about a specific person.",
213
+ "parameters": {
214
+ "type": "object",
215
+ "properties": {
216
+ "person_id": {
217
+ "type": "integer",
218
+ "description": "The ID of the person to get details for"
219
+ },
220
+ "language": {
221
+ "type": "string",
222
+ "description": "Language for the person details",
223
+ "default": "en-US"
224
+ },
225
+ "append_to_response": {
226
+ "type": "string",
227
+ "description": "Comma-separated list of additional details to append to the response (e.g., 'images,credits')"
228
+ }
229
+ },
230
+ "required": ["person_id"]
231
+ }
232
+ }
233
+ },
234
+ {
235
+ "type": "function",
236
+ "function": {
237
+ "name": "get_movie_genres",
238
+ "description": "Get the list of official genres for movies.",
239
+ "parameters": {
240
+ "type": "object",
241
+ "properties": {
242
+ "language": {
243
+ "type": "string",
244
+ "description": "Language for the genre names",
245
+ "default": "en-US"
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ ]