hadadrjt commited on
Commit
188764d
·
1 Parent(s): 06bbc18

ai: Implement dynamic host selection mechanism.

Browse files
app.py CHANGED
@@ -16,4 +16,4 @@ if __name__ == "__main__":
16
 
17
  # Call the 'launch' method on the 'app' object to start the user interface.
18
  # This typically opens the UI window or begins the event loop, making the application interactive.
19
- app.queue(default_concurrency_limit=None).launch(share=True, pwa=True)
 
16
 
17
  # Call the 'launch' method on the 'app' object to start the user interface.
18
  # This typically opens the UI window or begins the event loop, making the application interactive.
19
+ app.queue().launch(pwa=True)
src/client/chat_handler.py CHANGED
@@ -125,17 +125,28 @@ async def respond(
125
  return # Exit function after handling deep search
126
 
127
  # For all other inputs that do not match special commands, use the jarvis function to generate a normal response
128
- async for response in jarvis(
129
- session_id=session_id, # Session ID for conversation context
130
- model=selected_model, # Selected model for generation
131
- history=new_history, # Pass the conversation history
132
- user_message=input, # User input message
133
- mode=mode, # Use the mode determined by the thinking flag
134
- files=files, # Pass any attached files along with the message
135
- temperature=temperature, # temperature parameter
136
- top_k=top_k, # top_k parameter
137
- min_p=min_p, # min_p parameter
138
- top_p=top_p, # top_p parameter
139
- repetition_penalty=repetition_penalty # repetition_penalty parameter
140
- ):
141
- yield response # Yield each chunk of the response as it is generated by the AI model
 
 
 
 
 
 
 
 
 
 
 
 
125
  return # Exit function after handling deep search
126
 
127
  # For all other inputs that do not match special commands, use the jarvis function to generate a normal response
128
+ try:
129
+ async for response in jarvis(
130
+ session_id=session_id, # Session ID for conversation context
131
+ model=selected_model, # Selected model for generation
132
+ history=new_history, # Pass the conversation history
133
+ user_message=input, # User input message
134
+ mode=mode, # Use the mode determined by the thinking flag
135
+ files=files, # Pass any attached files along with the message
136
+ temperature=temperature, # temperature parameter
137
+ top_k=top_k, # top_k parameter
138
+ min_p=min_p, # min_p parameter
139
+ top_p=top_p, # top_p parameter
140
+ repetition_penalty=repetition_penalty # repetition_penalty parameter
141
+ ):
142
+ yield response # Yield each chunk of the response as it is generated by the AI model
143
+
144
+ # If all servers have been tried and failed, show the error message
145
+ except RuntimeError as e:
146
+ # Inform user of service unavailability
147
+ yield [{
148
+ "role": "assistant", # Specify the sender of this message
149
+ "content": "The server is currently busy. Please wait a moment or try again later"
150
+ # Provide a clear, user-friendly message explaining
151
+ }]
152
+ return # End the function after exhausting all servers
src/client/responses/audio.py CHANGED
@@ -84,7 +84,10 @@ async def audio_integration(
84
  "content": (
85
  "Audio generation failed for the user's request. The user tried to generate audio with the instruction: '"
86
  + audio_instruction + "'\n\n\n"
87
- "Please explain to the user that audio generation failed and suggest they wait 15 seconds before trying again.\n"
 
 
 
88
  "Be helpful and empathetic in your response.\n\n\n"
89
  "Use the same language as the previous user input or user request.\n"
90
  "For example, if the previous user input or user request is in Indonesian, explain in Indonesian.\n"
 
84
  "content": (
85
  "Audio generation failed for the user's request. The user tried to generate audio with the instruction: '"
86
  + audio_instruction + "'\n\n\n"
87
+ "Please explain to the user that the audio generation has failed.\n"
88
+ "Also, explain all possible reasons why it might have failed.\n"
89
+ "Additionally, advise the user not to include, input, or attach any sensitive content, "
90
+ "as this may also cause the audio generation process to fail.\n\n"
91
  "Be helpful and empathetic in your response.\n\n\n"
92
  "Use the same language as the previous user input or user request.\n"
93
  "For example, if the previous user input or user request is in Indonesian, explain in Indonesian.\n"
src/client/responses/image.py CHANGED
@@ -84,7 +84,10 @@ async def image_integration(
84
  "content": (
85
  "Image generation failed for the user's request. The user tried to generate an image with the instruction: '"
86
  + generate_image_instruction + "'\n\n\n"
87
- "Please explain to the user that image generation failed and suggest they wait 15 seconds before trying again.\n"
 
 
 
88
  "Be helpful and empathetic in your response.\n\n\n"
89
  "Use the same language as the previous user input or user request.\n"
90
  "For example, if the previous user input or user request is in Indonesian, explain in Indonesian.\n"
 
84
  "content": (
85
  "Image generation failed for the user's request. The user tried to generate an image with the instruction: '"
86
  + generate_image_instruction + "'\n\n\n"
87
+ "Please explain to the user that the image generation has failed.\n"
88
+ "Also, explain all possible reasons why it might have failed.\n"
89
+ "Additionally, advise the user not to include, input, or attach any sensitive content, "
90
+ "as this may also cause the image generation process to fail.\n\n"
91
  "Be helpful and empathetic in your response.\n\n\n"
92
  "Use the same language as the previous user input or user request.\n"
93
  "For example, if the previous user input or user request is in Indonesian, explain in Indonesian.\n"
src/core/server.py CHANGED
@@ -91,7 +91,14 @@ async def jarvis(
91
 
92
  # Attempt to stream the response using the primary HTTP transport method (httpx)
93
  try:
 
94
  async for chunk in httpx_transport(host, headers, payload, mode): # Stream response chunks asynchronously
 
 
 
 
 
 
95
  yield chunk # Yield each chunk to the caller as it arrives for real-time processing
96
  return # Exit the function if streaming completes successfully without errors
97
 
@@ -108,7 +115,14 @@ async def jarvis(
108
 
109
  # If the primary transport fails, attempt to stream response using fallback transport (aiohttp)
110
  try:
 
111
  async for chunk in aiohttp_transport(host, headers, payload, mode): # Use fallback streaming method
 
 
 
 
 
 
112
  yield chunk # Yield streamed chunks to caller as they arrive
113
  return # Exit if fallback transport succeeds
114
 
@@ -123,6 +137,5 @@ async def jarvis(
123
  except Exception: # Catch generic exceptions to avoid crashing
124
  mark(server) # Mark fallback server as failed
125
 
126
- # If all servers have been tried and failed, show the error message
127
- yield {"role": "assistant", "content": "The server is currently busy. Please wait a moment or try again later"} # Inform user of service unavailability
128
- return # End the function after exhausting all servers
 
91
 
92
  # Attempt to stream the response using the primary HTTP transport method (httpx)
93
  try:
94
+ # Iterate over each chunk of data yielded by the asynchronous httpx_transport function
95
  async for chunk in httpx_transport(host, headers, payload, mode): # Stream response chunks asynchronously
96
+ # Skip any None chunks to prevent errors
97
+ if chunk is None:
98
+ continue # Skip this iteration and wait for the next chunk without processing
99
+ # If the chunk is a dictionary but does not have a "content" key
100
+ if isinstance(chunk, dict) and "content" not in chunk:
101
+ chunk["content"] = "" # Add an empty "content" key to maintain a consistent data structure for downstream processing
102
  yield chunk # Yield each chunk to the caller as it arrives for real-time processing
103
  return # Exit the function if streaming completes successfully without errors
104
 
 
115
 
116
  # If the primary transport fails, attempt to stream response using fallback transport (aiohttp)
117
  try:
118
+ # Asynchronously iterate over chunks yielded by aiohttp_transport
119
  async for chunk in aiohttp_transport(host, headers, payload, mode): # Use fallback streaming method
120
+ # Skip any None chunks to prevent errors
121
+ if chunk is None:
122
+ continue # Skip this iteration and wait for the next chunk without processing
123
+ # If the chunk is a dictionary but does not have a "content" key
124
+ if isinstance(chunk, dict) and "content" not in chunk:
125
+ chunk["content"] = "" # Add an empty "content" key to maintain a consistent data structure for downstream processing
126
  yield chunk # Yield streamed chunks to caller as they arrive
127
  return # Exit if fallback transport succeeds
128
 
 
137
  except Exception: # Catch generic exceptions to avoid crashing
138
  mark(server) # Mark fallback server as failed
139
 
140
+ # If all servers have been tried and failed, raise an exception
141
+ raise RuntimeError() # End the function after exhausting all servers
 
src/tools/audio.py CHANGED
@@ -3,128 +3,91 @@
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
- import asyncio # Import asyncio to enable asynchronous programming, including async/await syntax and event loop management
7
- import httpx # Import httpx library to perform asynchronous HTTP requests with advanced features like connection pooling and timeout control
8
- import aiohttp # Import aiohttp library to provide an alternative asynchronous HTTP client for flexible request handling
9
- from urllib.parse import quote # Import quote function to safely encode strings for URL usage, escaping special characters
10
- from src.utils.ip_generator import generate_ip # Import a custom utility function to generate random IP addresses
11
- from config import auth # Import authentication credentials or configuration from the config module (not used directly here but imported for completeness)
12
- from src.utils.tools import initialize_tools # Import utility function to initialize and retrieve service endpoints or tool URLs
13
 
14
- # Define a class named AudioGeneration to encapsulate functionalities related to generating audio content
15
  class AudioGeneration:
16
- # This class provides methods to create audio files based on text instructions and voice parameters
17
-
18
  """
19
- This class provides methods to generate audio files from text instructions asynchronously.
20
- It supports retrying requests until successful audio generation is confirmed.
 
 
 
 
 
 
 
 
21
  """
22
-
23
- @staticmethod # This method does not depend on class instance state and can be called directly on the class
24
- async def create_audio(generate_audio_instruction: str, voice: str = "echo") -> str:
25
  """
26
- Asynchronously generate an audio file URL by sending a request to an audio generation service.
27
- The method continuously retries until it receives a successful HTTP 200 response with audio content.
28
-
 
 
 
 
 
 
 
 
 
 
29
  Args:
30
- generate_audio_instruction (str): The text instruction or content to convert into audio.
31
- voice (str, optional): The voice style or effect to apply on the generated audio. Defaults to "echo".
32
-
33
  Returns:
34
- str: The URL of the generated audio file upon successful retrieval.
35
-
36
  Raises:
37
- Exception: Currently, the method retries indefinitely and does not raise exceptions on failure.
38
  """
39
- # Encode the text instruction to make it safe for URL path inclusion by escaping special characters
40
  generate_audio_instruct = quote(generate_audio_instruction)
41
-
42
- # Initialize tools and extract the audio generation service endpoint (third element in the returned tuple)
43
  _, _, audio_tool, poll_token = initialize_tools()
44
-
45
- # Construct the full URL by appending the encoded instruction to the base audio tool URL
46
  url = f"{audio_tool}/{generate_audio_instruct}"
47
-
48
- # Define query parameters specifying the audio generation model and voice effect
49
  params = {
50
- "model": "openai-audio", # Specify the model used by the audio generation service
51
- "voice": voice # Specify the voice style or effect for the generated audio
52
  }
53
 
54
- # Create an aiohttp asynchronous HTTP client session with no timeout to allow long-running requests
55
- async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=None)) as session:
56
- # Enter an infinite loop to retry the request until success criteria are met
57
- while True:
58
- # Generate a random IP address to spoof the client's origin in the request headers
59
- headers = {
60
- "Authorization": f"Bearer {poll_token}", # Pollinations AI API Token
61
- "X-Forwarded-For": generate_ip() # Set the X-Forwarded-For header to a random IP address
62
- }
63
-
64
- try:
65
- # Perform an asynchronous GET request to the audio generation service with URL, parameters, and headers
66
- async with session.get(url, params=params, headers=headers) as resp:
67
- # Check if the response status code is 200 (OK) and content type indicates MPEG audio stream
68
- content_type = resp.headers.get('Content-Type', '')
69
- if resp.status == 200 and 'audio/mpeg' in content_type:
70
- # Return the final URL of the generated audio resource as a string
71
- return str(resp.url)
72
- else:
73
- # If the response is not successful or content type is unexpected, wait before retrying
74
- await asyncio.sleep(15) # Pause for 15 seconds to avoid overwhelming the server
75
- except aiohttp.ClientError:
76
- # Catch network-related errors such as connection issues and wait before retrying
77
- await asyncio.sleep(15) # Pause for 15 seconds before retrying after an exception
78
-
79
- @staticmethod # Provide an alternative implementation using httpx for flexibility or fallback
80
- async def create_audio_httpx(generate_audio_instruction: str, voice: str = "echo") -> str:
81
- """
82
- Alternative asynchronous method to generate audio using httpx client.
83
- This method also retries indefinitely until a successful response with audio content is received.
84
-
85
- Args:
86
- generate_audio_instruction (str): The text instruction to convert into audio.
87
- voice (str, optional): Voice style or effect. Defaults to "echo".
88
-
89
- Returns:
90
- str: URL of the generated audio file.
91
- """
92
- # Encode instruction for safe URL usage
93
- generate_audio_instruct = quote(generate_audio_instruction)
94
-
95
- # Initialize tools and get audio generation endpoint
96
- _, _, audio_tool, poll_token = initialize_tools()
97
-
98
- # Construct request URL
99
- url = f"{audio_tool}/{generate_audio_instruct}"
100
-
101
- # Define query parameters for the request
102
- params = {
103
- "model": "openai-audio",
104
- "voice": voice
105
  }
106
 
107
- # Create an asynchronous HTTP client with no timeout limit
108
- async with httpx.AsyncClient(timeout=None) as client:
109
- # Retry loop until success
110
- while True:
111
- # Define HTTP headers for the request, including random IP address to simulate different client origins
112
- headers = {
113
- "Authorization": f"Bearer {poll_token}", # Pollinations AI API Token
114
- "X-Forwarded-For": generate_ip() # Generate and set a random IP address for the request header
115
- }
116
-
117
- try:
118
- # Send GET request asynchronously
119
- resp = await client.get(url, params=params, headers=headers)
120
-
121
- # Check for successful response with audio content type
122
- if resp.status_code == 200 and 'audio/mpeg' in resp.headers.get('Content-Type', ''):
123
- # Return the URL of generated audio
124
- return str(resp.url)
125
- else:
126
- # Wait before retrying on failure
127
- await asyncio.sleep(15)
128
- except httpx.RequestError:
129
- # Handle network errors and wait before retrying
130
- await asyncio.sleep(15)
 
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
+ import aiohttp # Import aiohttp library to handle asynchronous HTTP requests and responses
7
+ from urllib.parse import quote # Import quote function to safely encode URL query parameters
8
+ from src.utils.ip_generator import generate_ip # Import custom utility to generate random IP addresses for request headers
9
+ from src.utils.tools import initialize_tools # Import initialize_tools function to set up necessary API tools and retrieve tokens
 
 
 
10
 
11
+ # Define a class to handle audio generation tasks asynchronously
12
  class AudioGeneration:
 
 
13
  """
14
+ This class provides functionality to generate audio content based on textual instructions asynchronously.
15
+ It encapsulates all necessary steps including preparing the request, encoding parameters, initializing tools,
16
+ making HTTP requests to the audio generation service, and handling the response.
17
+
18
+ The design leverages asynchronous programming to efficiently manage network I/O without blocking the main thread.
19
+ It uses external utilities for IP generation to simulate diverse client requests and for tool initialization to
20
+ obtain API endpoints and authorization tokens securely.
21
+
22
+ This class is intended for integration in larger applications requiring dynamic audio content creation from text,
23
+ supporting scalability and responsiveness in asynchronous environments.
24
  """
25
+ @staticmethod # Decorator indicating this method does not depend on instance state
26
+ # Asynchronous method to create an audio based on given instructions and parameters
27
+ async def create_audio(generate_audio_instruction: str) -> str:
28
  """
29
+ Asynchronously generates an audio URL from a given textual instruction describing the desired audio content.
30
+
31
+ This method performs the following steps:
32
+ - Encodes the input text to ensure it is safe for URL transmission
33
+ - Initializes necessary tools and retrieves the audio generation endpoint and authorization token
34
+ - Constructs the request URL and query parameters specifying the audio model and voice characteristics
35
+ - Sets up HTTP headers including authorization and IP address to simulate client diversity
36
+ - Opens an asynchronous HTTP session and sends a GET request to the audio generation service
37
+ - Checks the response status and content type to verify successful audio generation
38
+ - Returns the URL of the generated audio if successful, otherwise raises an exception
39
+
40
+ The method is designed to be non-blocking and suitable for use in asynchronous workflows.
41
+
42
  Args:
43
+ generate_audio_instruction (str): A textual description or instruction for the audio to be generated
44
+
 
45
  Returns:
46
+ str: The URL pointing to the generated audio resource
47
+
48
  Raises:
49
+ Exception: If the HTTP response indicates failure or the content is not audio
50
  """
51
+ # Encode the textual instruction to be safely included in a URL path segment
52
  generate_audio_instruct = quote(generate_audio_instruction)
53
+
54
+ # Initialize external tools and retrieve the audio generation endpoint and authorization token
55
  _, _, audio_tool, poll_token = initialize_tools()
56
+
57
+ # Construct the full URL by appending the encoded instruction to the audio tool base URL
58
  url = f"{audio_tool}/{generate_audio_instruct}"
59
+
60
+ # Define query parameters specifying the audio generation model and voice style to be used
61
  params = {
62
+ "model": "openai-audio", # Specify the audio generation model identifier
63
+ "voice": "echo" # Specify the voice style or effect to apply to the generated audio
64
  }
65
 
66
+ # Prepare HTTP headers
67
+ headers = {
68
+ "Authorization": f"Bearer {poll_token}", # Bearer token for API authorization
69
+ "X-Forwarded-For": generate_ip() # Random IP address for request header to simulate client origin
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
 
72
+ # Create an asynchronous HTTP client session to manage connections efficiently
73
+ async with aiohttp.ClientSession() as session:
74
+ """
75
+ This block manages the lifecycle of the HTTP client session, ensuring proper connection handling,
76
+ resource cleanup, and support for asynchronous request execution to avoid blocking the event loop.
77
+ """
78
+ # Send an asynchronous GET request to the audio generation service with specified URL, parameters, and headers
79
+ async with session.get(
80
+ url, # The fully constructed URL including the encoded instruction
81
+ params=params, # Query parameters dictating model and voice options
82
+ headers=headers # Authorization and client identity headers
83
+ ) as resp:
84
+ """
85
+ This context manages the HTTP response object, allowing inspection of status, headers,
86
+ and content in an asynchronous manner, suitable for streaming or large payloads.
87
+ """
88
+ # Verify that the response status is HTTP 200 OK and the content type indicates an audio MPEG file
89
+ if resp.status == 200 and 'audio/mpeg' in resp.headers.get('Content-Type', ''):
90
+ # Return the final URL from which the generated audio can be accessed or downloaded
91
+ return str(resp.url)
92
+ # If the response is not successful or content type is unexpected, raise an exception
93
+ raise Exception()
 
 
src/tools/image.py CHANGED
@@ -3,128 +3,102 @@
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
- import asyncio # Import asyncio for asynchronous programming and managing event loops
7
- import httpx # Import httpx for async HTTP requests with HTTP/1.1 and HTTP/2 support
8
- import aiohttp # Import aiohttp for alternative async HTTP client capabilities
9
- from urllib.parse import quote # Import quote to safely encode URL path components
10
- from typing import Optional # Import Optional for type hinting parameters that may be None
11
  from src.utils.ip_generator import generate_ip # Import custom utility to generate random IP addresses for request headers
12
- from src.utils.tools import initialize_tools # Import utility to initialize and get tool endpoints
13
 
14
- # Define a class named ImageGeneration to encapsulate functionalities related to generating image content
15
  class ImageGeneration:
16
- # This class provides methods to create image files based on text instructions
17
-
18
  """
19
- Class to handle asynchronous image generation requests to an external service.
20
-
21
- Attributes:
22
- FORMATS (dict): Maps image format names to (width, height) tuples.
23
-
24
- Methods:
25
- create_image: Async method to generate an image URL from a text prompt,
26
- retrying until successful, using httpx and aiohttp.
27
  """
28
-
29
- # Supported image formats with their dimensions (width, height)
30
  FORMATS = {
31
- "default": (1024, 1024),
32
- "square": (1024, 1024),
33
- "landscape": (1024, 768),
34
- "landscape_large": (1440, 1024),
35
- "portrait": (768, 1024),
36
- "portrait_large": (1024, 1440),
37
  }
38
 
39
- @staticmethod
 
40
  async def create_image(
41
- generate_image_instruction: str, # Text description for the image to generate
42
- image_format: str = "default", # Format key from FORMATS dict
43
- model: Optional[str] = "flux-realism", # Model name for generation, default 'flux-realism'
44
- seed: Optional[int] = None, # Optional seed for reproducible randomness
45
- nologo: bool = True, # Whether to exclude logo watermark
46
- private: bool = True, # Whether the image should be private
47
- enhance: bool = True, # Whether to apply enhancement filters
48
  ) -> str:
49
  """
50
- Asynchronously generate an image URL by sending requests to the image generation service.
51
- Uses httpx for initial requests and aiohttp as fallback, retrying indefinitely until success.
52
-
53
- Args:
54
- generate_image_instruction (str): Text prompt describing the desired image.
55
- image_format (str): Key for image dimensions.
56
- model (Optional[str]): Model to use for generation.
57
- seed (Optional[int]): Seed for randomization control.
58
- nologo (bool): Flag to exclude logo watermark.
59
- private (bool): Flag to mark image as private.
60
- enhance (bool): Flag to apply image enhancement.
61
-
62
- Returns:
63
- str: URL of the generated image on success.
64
-
65
- Raises:
66
- ValueError: If image_format is invalid.
67
  """
68
- # Validate image format key
69
  if image_format not in ImageGeneration.FORMATS:
70
- raise ValueError("Invalid image format.")
71
 
72
- # Extract width and height for the requested format
73
  width, height = ImageGeneration.FORMATS[image_format]
74
 
75
- # Initialize tools and get image generation service endpoint URL
76
- _, image_tool, poll_token, _ = initialize_tools()
77
-
78
- # Encode instruction safely for URL path usage
79
- generate_image_instruct = quote(generate_image_instruction)
80
 
81
- # Construct the full URL endpoint for image generation
82
- url = f"{image_tool}{generate_image_instruct}"
83
 
84
- # Prepare query parameters with image size, model, flags as strings
85
  params = {
86
- "width": width,
87
- "height": height,
88
- "model": model,
89
- "nologo": "true" if nologo else "false",
90
- "private": "true" if private else "false",
91
- "enhance": "true" if enhance else "false",
92
  }
93
 
94
- # Add seed parameter if provided
95
  if seed is not None:
96
  params["seed"] = seed
97
 
98
- # Prepare headers
99
  headers = {
100
- "Authorization": f"Bearer {poll_token}", # Pollinations AI API Token
101
  "X-Forwarded-For": generate_ip() # Random IP address for request header to simulate client origin
102
  }
103
 
104
- # Use httpx.AsyncClient with no timeout for initial requests
105
- async with httpx.AsyncClient(timeout=None) as client:
106
- while True:
107
- try:
108
- # Send GET request to the image generation endpoint
109
- resp = await client.get(url, params=params, headers=headers)
110
-
111
- # If response is successful, return the final URL
112
- if resp.status_code == 200:
113
- return str(resp.url)
114
- except httpx.HTTPError:
115
- # On httpx errors, fallback to aiohttp for robustness
116
- pass
117
-
118
- # Fallback retry with aiohttp client
119
- async with aiohttp.ClientSession() as session:
120
- try:
121
- async with session.get(url, params=params, headers=headers) as resp:
122
- if resp.status == 200:
123
- # Return the final URL (aiohttp does not provide direct URL property)
124
- return str(resp.url)
125
- except aiohttp.ClientError:
126
- # Ignore aiohttp errors and retry
127
- pass
128
-
129
- # Wait 15 seconds before retrying to avoid overwhelming the server
130
- await asyncio.sleep(15)
 
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
+ import aiohttp # Import aiohttp library for asynchronous HTTP client functionality
7
+ from urllib.parse import quote # Import quote function to URL-encode query parameters safely
8
+ from typing import Optional # Import Optional type hint for parameters that may be None
 
 
9
  from src.utils.ip_generator import generate_ip # Import custom utility to generate random IP addresses for request headers
10
+ from src.utils.tools import initialize_tools # Import custom function to initialize necessary tools and tokens
11
 
12
+ # Define a class to handle image generation requests asynchronously
13
  class ImageGeneration:
 
 
14
  """
15
+ This class provides a comprehensive interface for generating images asynchronously via HTTP requests.
16
+ It supports multiple image formats with predefined dimensions and allows customization of generation parameters such as model type, seed, and enhancement options.
17
+ The class handles URL encoding, authentication headers, and IP generation to simulate diverse client requests.
18
+ It uses aiohttp for efficient asynchronous HTTP client sessions and manages error handling for invalid inputs and HTTP response failures.
 
 
 
 
19
  """
20
+ # Dictionary mapping image format names to their corresponding width and height dimensions
 
21
  FORMATS = {
22
+ "default": (1024, 1024), # Default square format with 1024x1024 pixels
23
+ "square": (1024, 1024), # Square format identical to default size
24
+ "landscape": (1024, 768), # Landscape format with width greater than height
25
+ "landscape_large": (1440, 1024), # Larger landscape format for higher resolution images
26
+ "portrait": (768, 1024), # Portrait format with height greater than width
27
+ "portrait_large": (1024, 1440), # Larger portrait format for higher resolution images
28
  }
29
 
30
+ @staticmethod # Decorator indicating this method does not depend on instance state
31
+ # Asynchronous method to create an image based on given instructions and parameters
32
  async def create_image(
33
+ generate_image_instruction: str, # Text instruction describing the image to generate
34
+ image_format: str = "default", # Desired image format key from FORMATS dictionary
35
+ model: Optional[str] = "flux", # Optional model identifier controlling generation style or algorithm
36
+ seed: Optional[int] = None, # Optional random seed to reproduce specific image outputs
37
+ nologo: bool = True, # Flag to remove logo watermark from generated image
38
+ private: bool = True, # Flag to mark image generation as private, restricting visibility
39
+ enhance: bool = True, # Flag to apply enhancement filters to improve image quality
40
  ) -> str:
41
  """
42
+ This asynchronous method generates an image URL based on detailed instructions and customization options.
43
+ It validates the requested image format against supported formats, retrieves corresponding dimensions,
44
+ initializes necessary tools and authentication tokens, constructs the request URL with encoded parameters,
45
+ and performs an HTTP GET request asynchronously using aiohttp.
46
+ The method includes headers for authorization and IP address to simulate requests from different clients.
47
+ On successful response (HTTP 200), it returns the final URL of the generated image.
48
+ If the response status is not successful, it raise an exception.
 
 
 
 
 
 
 
 
 
 
49
  """
50
+ # Validate if the requested image format is supported, otherwise raise an error
51
  if image_format not in ImageGeneration.FORMATS:
52
+ raise ValueError("Invalid image format")
53
 
54
+ # Retrieve width and height dimensions for the requested image format
55
  width, height = ImageGeneration.FORMATS[image_format]
56
 
57
+ # Initialize external tools and retrieve the image tool URL and authorization token
58
+ _, image_tool, _, poll_token = initialize_tools()
 
 
 
59
 
60
+ # Construct the base URL for the image generation request with URL-encoded instruction
61
+ url = f"{image_tool}{quote(generate_image_instruction)}"
62
 
63
+ # Prepare query parameters dictionary for the HTTP request with all relevant options
64
  params = {
65
+ "width": width, # Image width in pixels
66
+ "height": height, # Image height in pixels
67
+ "model": model, # Model identifier string for image generation
68
+ "nologo": str(nologo).lower(), # Convert boolean to lowercase string for query parameter
69
+ "private": str(private).lower(), # Convert boolean to lowercase string for query parameter
70
+ "enhance": str(enhance).lower(), # Convert boolean to lowercase string for query parameter
71
  }
72
 
73
+ # If a seed value is provided, add it to the query parameters to control randomness
74
  if seed is not None:
75
  params["seed"] = seed
76
 
77
+ # Prepare HTTP headers
78
  headers = {
79
+ "Authorization": f"Bearer {poll_token}", # Bearer token for API authentication
80
  "X-Forwarded-For": generate_ip() # Random IP address for request header to simulate client origin
81
  }
82
 
83
+ # Create an asynchronous HTTP client session to perform the GET request
84
+ async with aiohttp.ClientSession() as session:
85
+ """
86
+ This context manager ensures that the HTTP session is properly opened and closed asynchronously,
87
+ enabling efficient connection reuse and resource cleanup after the request completes.
88
+ """
89
+ # Perform the HTTP GET request asynchronously with the constructed URL, parameters, and headers
90
+ async with session.get(
91
+ url, # The fully constructed URL including encoded instruction
92
+ params=params, # Query parameters controlling image generation options
93
+ headers=headers # Authorization and client identity headers
94
+ ) as resp:
95
+ """
96
+ This context manager handles the HTTP response asynchronously.
97
+ It waits for the server's response and manages the response lifecycle.
98
+ """
99
+ # Check if the HTTP response status indicates success
100
+ if resp.status == 200 and 'image/' in resp.headers.get('Content-Type', ''):
101
+ # Return the final URL of the generated image as a string
102
+ return str(resp.url)
103
+ # If the response is not successful or content type is unexpected, raise an exception
104
+ raise Exception()
 
 
 
 
 
src/ui/interface.py CHANGED
@@ -3,159 +3,209 @@
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
- import gradio as gr # Import the Gradio library to build interactive web interfaces for machine learning applications
7
- from src.core.parameter import parameters # Import the 'parameters' function from the core parameter module, which returns model parameter settings based on reasoning mode
8
- from src.client.chat_handler import respond # Import the 'respond' function from the chat handler module, responsible for generating AI assistant responses
9
- from config import model, meta_tags # Import 'model' dictionary containing available model precision options and their details, and 'meta_tags' containing HTML meta tag data
10
-
11
- # Gradio
 
 
 
 
12
  def ui():
13
  """
14
- Constructs the Gradio user interface for the J.A.R.V.I.S. AI assistant application.
15
-
16
- This function sets up a web app with a sidebar for configuring model parameters and a main chat interface
17
- for user interaction. It returns the Gradio Blocks object representing the entire app.
 
 
 
 
 
18
  """
19
- # Create a Gradio Blocks container that fills the entire available height and width of the browser window
20
- with gr.Blocks(fill_height=True, fill_width=True, head=meta_tags) as app:
21
- # Create a sidebar panel on the left side, initially closed, to hold model configuration controls
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  with gr.Sidebar(open=False):
23
- # Dropdown menu for selecting the model precision from the keys of the 'model' dictionary
24
  model_precision = gr.Dropdown(
25
- choices=list(model.keys()), # List of available model precision options, e.g., "F16", "F32"
26
- label="Model Precision", # Label displayed above the dropdown menu
27
  info=(
28
- # Tooltip explaining the tradeoff between speed and accuracy based on precision choice
29
  "The smaller the value, the faster the response but less accurate. "
30
  "Conversely, the larger the value, the response is slower but more accurate."
31
  ),
32
- value="Q8_K_XL" # Default selected precision value
33
  )
34
-
35
- # Checkbox to enable or disable reasoning mode, which toggles the AI's "thinking" capability
36
  reasoning = gr.Checkbox(
37
- label="Reasoning", # Label shown next to the checkbox
38
- info="Switching between thinking and non-thinking mode.", # Tooltip describing the feature
39
- value=True # Default state is enabled (checked)
40
  )
41
-
42
- # Slider controlling the 'Temperature' parameter, affecting randomness in AI responses, initially non-interactive
43
  temperature = gr.Slider(
44
- minimum=0.0, # Minimum slider value
45
- maximum=2.0, # Maximum slider value
46
- step=0.01, # Increment step size
47
- label="Temperature", # Label for the slider
48
- interactive=False # User cannot directly adjust this slider, updated dynamically
49
  )
50
-
51
- # Slider controlling the 'Top K' parameter, which limits the number of highest probability tokens considered, non-interactive initially
52
  top_k = gr.Slider(
53
- minimum=0,
54
- maximum=100,
55
- step=1,
56
- label="Top K",
57
- interactive=False
58
  )
59
-
60
- # Slider for 'Min P' parameter, representing minimum cumulative probability threshold, non-interactive initially
61
  min_p = gr.Slider(
62
- minimum=0.0,
63
- maximum=1.0,
64
- step=0.01,
65
- label="Min P",
66
- interactive=False
67
  )
68
-
69
- # Slider for 'Top P' parameter, controlling nucleus sampling probability, non-interactive initially
70
  top_p = gr.Slider(
71
- minimum=0.0,
72
- maximum=1.0,
73
- step=0.01,
74
- label="Top P",
75
- interactive=False
76
  )
77
-
78
- # Slider for 'Repetition Penalty' parameter to reduce repetitive text generation, non-interactive initially
79
  repetition_penalty = gr.Slider(
80
- minimum=0.1,
81
- maximum=2.0,
82
- step=0.01,
83
- label="Repetition Penalty",
84
- interactive=False
85
  )
86
-
87
- # Define a function to update the model parameter sliders based on the reasoning checkbox state
88
  def update_parameters(switching):
89
  """
90
- Retrieve updated model parameter values based on reasoning mode.
91
-
92
- Args:
93
- switching (bool): Current state of the reasoning checkbox.
94
-
95
- Returns:
96
- tuple: Updated values for temperature, top_k, min_p, top_p, and repetition_penalty sliders.
97
  """
98
- # Call the 'parameters' function passing the reasoning state to get new parameter values
99
  return parameters(switching)
100
-
101
- # Set up an event listener to update parameter sliders when the reasoning checkbox state changes
102
  reasoning.change(
103
- fn=update_parameters, # Function to call on checkbox state change
104
- inputs=[reasoning], # Input is the reasoning checkbox's current value
105
- outputs=[temperature, top_k, min_p, top_p, repetition_penalty], # Update these sliders with new values
106
- api_name=False # Disable API
107
  )
108
-
109
- # Initialize the parameter sliders with values corresponding to the default reasoning checkbox state
110
  values = parameters(reasoning.value)
111
  temperature.value, top_k.value, min_p.value, top_p.value, repetition_penalty.value = values
112
-
113
- # Checkbox to enable or disable the image generation feature in the chat interface
114
  image_generation = gr.Checkbox(
115
- label="Image Generation", # Label displayed next to the checkbox
116
  info=(
117
- # Tooltip explaining how to trigger image generation via chat commands
118
  "Type <i><b>/image</b></i> followed by the instructions to start generating an image."
119
  ),
120
- value=True # Enabled by default
121
  )
122
-
123
- # Checkbox to enable or disable the audio generation feature in the chat interface
124
  audio_generation = gr.Checkbox(
125
- label="Audio Generation",
126
  info=(
 
127
  "Type <i><b>/audio</b></i> followed by the instructions to start generating audio."
128
  ),
129
- value=True
130
  )
131
-
132
- # Checkbox to enable or disable the deep web search feature in the chat interface
133
  search_generation = gr.Checkbox(
134
- label="Deep Search",
135
  info=(
 
136
  "Type <i><b>/dp</b></i> followed by the instructions to search the web."
137
  ),
138
- value=True
139
  )
140
-
141
- # Create the main chat interface where users interact with the AI assistant
142
  gr.ChatInterface(
143
- fn=respond, # Function called to generate responses to user inputs
 
144
  additional_inputs=[
145
- # Pass the current states of all configuration controls as additional inputs to the respond function
146
- model_precision,
147
- temperature,
148
- top_k,
149
- min_p,
150
- top_p,
151
- repetition_penalty,
152
- reasoning,
153
- image_generation,
154
- audio_generation,
155
- search_generation
156
  ],
 
157
  examples=[
158
- # Predefined example inputs to help users quickly test the assistant's features
159
  ["Please introduce yourself."],
160
  ["/audio Could you explain what Artificial Intelligence (AI) is?"],
161
  ["/audio What is Hugging Face?"],
@@ -170,20 +220,15 @@ def ui():
170
  ["Please generate a highly complex code snippet on any topic."],
171
  ["Explain about quantum computers."]
172
  ],
173
- cache_examples=False, # Disable caching of example outputs to always generate fresh responses
174
  chatbot=gr.Chatbot(
175
- label="J.A.R.V.I.S.", # Title label displayed above the chat window
176
- show_copy_button=True, # Show a button allowing users to copy chat messages
177
- scale=1, # Scale factor for the chatbot UI size
178
- allow_tags=["think"] # Reasoning tag
179
  ),
180
- multimodal=False, # Disable support for multimodal inputs such as images or audio files
181
- fill_height=True, # Duplicate from Blocks to Chat Interface
182
- fill_width=True, # Duplicate from Blocks to Chat Interface
183
- head=meta_tags, # Duplicate from Blocks to Chat Interface
184
- show_progress="full", # Progress animation
185
- api_name="api", # API endpoint
186
- concurrency_limit=None # Queuing
187
  )
188
- # Return the complete Gradio app object for launching or embedding
189
  return app
 
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
+ import logging # Import the logging module to enable tracking of events, errors, and debugging information during program execution
7
+ import warnings # Import the warnings module to manage and control warning messages generated by the code or libraries
8
+ import os # Import the os module to interact with the operating system, such as file system operations and environment variables
9
+ import sys # Import the sys module to access system-specific parameters and functions, including command-line arguments and interpreter information
10
+ import gradio as gr # Import the Gradio library to build interactive web interfaces for machine learning applications, enabling easy deployment and testing
11
+ from src.core.parameter import parameters # Import the 'parameters' function from the core parameter module, which provides model parameter configurations based on the selected reasoning mode
12
+ from src.client.chat_handler import respond # Import the 'respond' function from the chat handler module, responsible for generating AI assistant responses to user inputs
13
+ from config import model, meta_tags # Import 'model' dictionary containing available model precision options and their details, and 'meta_tags' dictionary containing HTML meta tag information for web interface setup
14
+
15
+ # Define the main user interface function for the Gradio application
16
  def ui():
17
  """
18
+ This function constructs and configures the entire user interface for the AI assistant web application using the Gradio Blocks API.
19
+ The function is responsible for setting up logging and warning filters, suppressing unwanted output, and building a highly interactive sidebar for model configuration.
20
+ It provides users with a comprehensive set of controls to adjust model precision, reasoning mode, and advanced generation parameters such as temperature, top_k, min_p, top_p, and repetition penalty.
21
+ The sidebar also allows users to enable or disable additional features like image generation, audio generation, and deep web search, each with descriptive tooltips to guide user interaction.
22
+ The function dynamically updates parameter sliders based on the state of the reasoning checkbox, ensuring that the UI always reflects the current configuration.
23
+ The main chat interface is constructed with a set of example prompts to help users explore the assistant's capabilities, and all configuration states are passed as additional inputs to the response handler.
24
+ Output and error streams are redirected to prevent extraneous log messages from appearing in the web interface, creating a clean and focused user experience.
25
+ The function returns the fully constructed Gradio app object, ready to be launched or embedded as needed.
26
+ This approach ensures the application is robust, user-friendly, and highly customizable, making it suitable for both demonstration and production use
27
  """
28
+ # Suppress all warning messages globally to prevent them from displaying in the application output and confusing users
29
+ warnings.filterwarnings(
30
+ "ignore" # Specify the action to ignore all warnings, ensuring a clean user interface free from warning clutter
31
+ )
32
+ # Set the warnings filter to ignore warnings by default, providing an additional layer of suppression for any warnings that might arise during runtime
33
+ warnings.simplefilter(
34
+ "ignore" # Apply the ignore filter to all warning categories, further ensuring that no warnings are shown to users
35
+ )
36
+ # Configure the logging system to only display messages at the CRITICAL level or higher, effectively hiding informational, warning, and error logs from the application's output
37
+ logging.basicConfig(
38
+ level=logging.CRITICAL # Set the logging threshold to CRITICAL, which is the highest severity level, to minimize log output
39
+ )
40
+ # Define a list of logger names associated with common web and async frameworks used in Gradio apps
41
+ logger_keywords = [
42
+ "uvicorn", # Uvicorn is an ASGI web server commonly used with FastAPI and Gradio
43
+ "fastapi", # FastAPI is a modern web framework for building APIs with Python
44
+ "gradio", # Gradio's own logger, which may emit internal messages
45
+ "httpx", # HTTPX is an HTTP client for Python, often used for making web requests
46
+ "aiohttp", # Aiohttp is an asynchronous HTTP client and server library
47
+ "asyncio", # Asyncio is Python's standard library for asynchronous programming
48
+ "starlette", # Starlette is a lightweight ASGI framework used by FastAPI
49
+ "anyio", # AnyIO is an asynchronous networking and concurrency library
50
+ ]
51
+ # Iterate through all registered loggers in the logging system to suppress their output if they match any of the specified keywords
52
+ for name, logger in logging.root.manager.loggerDict.items():
53
+ # Check if the logger's name contains any of the keywords from the list, indicating it is associated with a third-party library whose logs should be suppressed
54
+ if any(k in name for k in logger_keywords):
55
+ # Ensure the logger object is an instance of the Logger class before attempting to set its level
56
+ if isinstance(
57
+ logger, # The logger object retrieved from the logger dictionary
58
+ logging.Logger # The Logger class from the logging module
59
+ ):
60
+ # Set the logger's level to CRITICAL to prevent it from emitting lower-severity log messages during application execution
61
+ logger.setLevel(
62
+ logging.CRITICAL # Only allow critical errors to be logged, hiding all informational and warning messages
63
+ )
64
+ # Redirect the standard output stream to the null device, effectively silencing all print statements and unwanted output from third-party libraries
65
+ sys.stdout = open(
66
+ os.devnull, "w" # Open the operating system's null device in write mode, discarding any data written to it
67
+ )
68
+ # Redirect the standard error stream to the null device, ensuring that error messages and tracebacks do not appear in the application's output
69
+ sys.stderr = open(
70
+ os.devnull, "w" # Open the null device for error output, suppressing all error messages from being displayed to users
71
+ )
72
+ # Create the main Gradio Blocks application, which serves as the container for all UI components and layout
73
+ with gr.Blocks(
74
+ fill_height=True, # Automatically adjust the block's height to fill the available vertical space in the browser window, ensuring a responsive design
75
+ fill_width=True, # Automatically adjust the block's width to fill the available horizontal space, maximizing the use of screen real estate
76
+ head=meta_tags # Inject custom meta tags into the HTML head section, allowing for SEO optimization
77
+ ) as app:
78
+ """
79
+ This code block initializes the main Gradio Blocks context, which acts as the root container for all user interface components in the application.
80
+ The fill_height and fill_width parameters ensure that the app dynamically resizes to fit the user's browser window, providing a seamless and visually appealing experience.
81
+ The head parameter inserts custom meta tags into the HTML head, enabling advanced customization such as setting the page title, description, favicon, or viewport settings.
82
+ All UI elements, including the sidebar configuration controls and the main chat interface, are defined within this context to ensure they are properly managed and rendered by Gradio.
83
+ The resulting 'app' object encapsulates the entire interactive application, making it easy to launch or embed as needed
84
+ """
85
+ # Begin the sidebar section, which slides in from the left and contains all model configuration controls. The sidebar starts in a closed state for a cleaner initial appearance
86
  with gr.Sidebar(open=False):
87
+ # Create a dropdown menu that allows users to select the desired model precision from the available options defined in the 'model' dictionary
88
  model_precision = gr.Dropdown(
89
+ choices=list(model.keys()), # Populate the dropdown with the keys from the model dictionary, such as "F16", "F32", or custom precision names
90
+ label="Model Precision", # Display this label above the dropdown to clearly indicate its purpose to users
91
  info=(
92
+ # Provide a tooltip that explains the impact of different precision settings on model speed and accuracy, helping users make informed choices
93
  "The smaller the value, the faster the response but less accurate. "
94
  "Conversely, the larger the value, the response is slower but more accurate."
95
  ),
96
+ value="Q8_K_XL" # Set the default selected value to "Q8_K_XL", which represents a specific model precision configuration
97
  )
98
+ # Add a checkbox that enables or disables reasoning mode, which affects how the AI assistant processes user queries and generates responses
 
99
  reasoning = gr.Checkbox(
100
+ label="Reasoning", # Show this label next to the checkbox to indicate its function
101
+ info="Switching between thinking and non-thinking mode.", # Display a tooltip explaining that this option toggles the AI's ability to perform complex reasoning
102
+ value=True # Set the default state to enabled, so the AI uses reasoning mode by default
103
  )
104
+ # Create a slider for the 'Temperature' parameter, which controls the randomness of the AI's text generation. The slider is initially non-interactive and updated dynamically based on reasoning mode
 
105
  temperature = gr.Slider(
106
+ minimum=0.0, # Allow the temperature value to range from 0.0, representing deterministic output
107
+ maximum=2.0, # Set the upper limit to 2.0, allowing for highly random and creative responses
108
+ step=0.01, # Allow fine-grained adjustments in increments of 0.01 for precise control
109
+ label="Temperature", # Label the slider so users know what parameter they are adjusting
110
+ interactive=False # Disable direct user interaction, as the value is set programmatically when reasoning mode changes
111
  )
112
+ # Add a slider for the 'Top K' parameter, which limits the number of highest-probability tokens considered during text generation. This helps control output diversity
 
113
  top_k = gr.Slider(
114
+ minimum=0, # Allow the top_k value to start at 0, which may disable the filter
115
+ maximum=100, # Set the maximum to 100, providing a wide range for experimentation with model output diversity
116
+ step=1, # Adjust the value in whole number increments for clarity
117
+ label="Top K", # Label the slider for user understanding
118
+ interactive=False # Make the slider non-interactive, as it is updated based on reasoning mode
119
  )
120
+ # Create a slider for the 'Min P' parameter, representing the minimum cumulative probability threshold for token selection during generation
 
121
  min_p = gr.Slider(
122
+ minimum=0.0, # Allow the minimum probability to start at 0.0, including all tokens
123
+ maximum=1.0, # Set the maximum to 1.0, representing the full probability mass
124
+ step=0.01, # Use small increments for precise tuning
125
+ label="Min P", # Label the slider to clarify its function
126
+ interactive=False # Disable direct interaction, as it is controlled programmatically
127
  )
128
+ # Add a slider for the 'Top P' parameter, which controls nucleus sampling by limiting the cumulative probability of selected tokens
 
129
  top_p = gr.Slider(
130
+ minimum=0.0, # Allow the top_p value to start at 0.0, which may restrict output to only the most probable token
131
+ maximum=1.0, # Set the upper limit to 1.0, including all tokens in the sampling pool
132
+ step=0.01, # Enable fine adjustments for optimal output control
133
+ label="Top P", # Label the slider accordingly
134
+ interactive=False # Make the slider non-interactive, as it is set based on reasoning mode
135
  )
136
+ # Create a slider for the 'Repetition Penalty' parameter, which discourages the model from generating repetitive text by penalizing repeated tokens
 
137
  repetition_penalty = gr.Slider(
138
+ minimum=0.1, # Set the minimum penalty to 0.1, allowing for low but non-zero penalties
139
+ maximum=2.0, # Allow penalties up to 2.0, strongly discouraging repetition
140
+ step=0.01, # Use small increments for precise adjustment
141
+ label="Repetition Penalty", # Label the slider for clarity
142
+ interactive=False # Make the slider non-interactive, as it is updated programmatically
143
  )
144
+ # Define a function to update all parameter sliders when the reasoning checkbox is toggled by the user
 
145
  def update_parameters(switching):
146
  """
147
+ This function is triggered whenever the user changes the state of the reasoning checkbox in the sidebar.
148
+ It calls the 'parameters' function, passing the new reasoning state, to retrieve a tuple of updated values for temperature, top_k, min_p, top_p, and repetition penalty.
149
+ The returned values are then used to update the corresponding sliders in the sidebar, ensuring that the UI always reflects the current configuration for the selected reasoning mode.
150
+ This dynamic updating mechanism provides immediate feedback to users and helps prevent configuration mismatches, improving the overall user experience and reliability of the application
 
 
 
151
  """
152
+ # Call the external 'parameters' function with the current reasoning state to get updated parameter values
153
  return parameters(switching)
154
+ # Set up an event listener that calls 'update_parameters' whenever the reasoning checkbox state changes, updating all related sliders with new values
 
155
  reasoning.change(
156
+ fn=update_parameters, # Specify the function to call when the checkbox value changes
157
+ inputs=[reasoning], # Pass the current value of the reasoning checkbox as input
158
+ outputs=[temperature, top_k, min_p, top_p, repetition_penalty], # Update all relevant sliders with the new parameter values
159
+ api_name=False # Disable API exposure for this event, as it is only used internally by the UI
160
  )
161
+ # Initialize all parameter sliders with values corresponding to the default state of the reasoning checkbox, ensuring the UI is consistent on first load
 
162
  values = parameters(reasoning.value)
163
  temperature.value, top_k.value, min_p.value, top_p.value, repetition_penalty.value = values
164
+ # Add a checkbox to enable or disable image generation capabilities in the chat interface, allowing users to control access to this feature
 
165
  image_generation = gr.Checkbox(
166
+ label="Image Generation", # Display this label next to the checkbox for clarity
167
  info=(
168
+ # Provide a tooltip explaining how to trigger image generation using a special chat command
169
  "Type <i><b>/image</b></i> followed by the instructions to start generating an image."
170
  ),
171
+ value=True # Enable image generation by default for user convenience
172
  )
173
+ # Add a checkbox to enable or disable audio generation in the chat, giving users control over this feature
 
174
  audio_generation = gr.Checkbox(
175
+ label="Audio Generation", # Label the checkbox for clarity
176
  info=(
177
+ # Tooltip instructing users to use the /audio command to generate audio responses
178
  "Type <i><b>/audio</b></i> followed by the instructions to start generating audio."
179
  ),
180
+ value=True # Enable audio generation by default
181
  )
182
+ # Add a checkbox to enable or disable deep web search functionality in the chat interface
 
183
  search_generation = gr.Checkbox(
184
+ label="Deep Search", # Label the checkbox to indicate its purpose
185
  info=(
186
+ # Tooltip explaining how to trigger deep search using the /dp command in chat
187
  "Type <i><b>/dp</b></i> followed by the instructions to search the web."
188
  ),
189
+ value=True # Enable deep search by default
190
  )
191
+ # Create the main chat interface where users can interact with the AI assistant, send messages, and receive responses
 
192
  gr.ChatInterface(
193
+ fn=respond, # Specify the function that processes user input and generates assistant responses
194
+ # Provide a list of additional input components whose current values are passed to the respond function for each user message
195
  additional_inputs=[
196
+ model_precision, # Current selected model precision value from the dropdown
197
+ temperature, # Current temperature value from the slider
198
+ top_k, # Current top_k value from the slider
199
+ min_p, # Current min_p value from the slider
200
+ top_p, # Current top_p value from the slider
201
+ repetition_penalty, # Current repetition penalty value from the slider
202
+ reasoning, # Current state of the reasoning checkbox
203
+ image_generation, # Whether image generation is enabled
204
+ audio_generation, # Whether audio generation is enabled
205
+ search_generation # Whether deep search is enabled
 
206
  ],
207
+ # Provide a list of example prompts that users can click to quickly test the assistant's capabilities and explore different features
208
  examples=[
 
209
  ["Please introduce yourself."],
210
  ["/audio Could you explain what Artificial Intelligence (AI) is?"],
211
  ["/audio What is Hugging Face?"],
 
220
  ["Please generate a highly complex code snippet on any topic."],
221
  ["Explain about quantum computers."]
222
  ],
223
+ cache_examples=False, # Disable caching of example outputs to ensure fresh responses are generated each time an example is selected
224
  chatbot=gr.Chatbot(
225
+ label="J.A.R.V.I.S.", # Set the title label above the chat window to "J.A.R.V.I.S." for branding and recognition
226
+ show_copy_button=True, # Display a button that allows users to easily copy chat messages for later use
227
+ scale=1, # Set the scale factor for the chatbot UI, keeping the default size
228
+ allow_tags=["think"] # Allow the use of the "think" tag to indicate reasoning mode in chat
229
  ),
230
+ multimodal=False, # Disable file upload capabilities, restricting the chat to text-only interactions
231
+ api_name="api", # Expose the chat interface as an API endpoint named "api" for programmatic access if needed
 
 
 
 
 
232
  )
233
+ # Return the fully constructed Gradio app object, which can be launched as a standalone web application or embedded in other platforms
234
  return app
src/utils/session_mapping.py CHANGED
@@ -3,74 +3,117 @@
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
- import random # Import random module to enable random selection from a list
7
- import gradio as gr # Import the Gradio library to build interactive web interfaces for machine learning applications
8
- from datetime import datetime # Import datetime class to work with current UTC time
9
- from typing import Dict, List # Import type hints for dictionaries and lists (not explicitly used here but imported)
10
- from config import auth # Import authentication configuration, likely a list of host dictionaries with credentials
11
- from src.utils.helper import busy, mark # Import 'busy' dictionary and 'mark' function to track and update host busy status
12
 
13
- # Initialize a global dictionary to map session IDs to assigned hosts
14
- mapping = {} # Store session_id to host assignment mapping to maintain consistent host allocation per session
15
 
16
- # Define a function to get an available host for a given session, optionally excluding certain hosts
17
  def get_host(session_id: str, exclude_hosts: List[str] = None) -> dict:
18
  """
19
- Retrieve or assign a host for the given session ID.
 
 
20
 
21
  Args:
22
- session_id (str): A unique identifier for the current session.
 
23
 
24
  Returns:
25
- dict: The selected host dictionary from the auth configuration.
26
 
27
- Raises:
28
- Exception: If no available hosts are found to assign.
 
 
29
 
30
- Explanation:
31
- Retrieve an available host for the specified session ID, ensuring excluded hosts are not assigned.
32
- This function maintains a mapping of session IDs to hosts to provide consistent host assignment.
33
- It filters out busy hosts and those explicitly excluded, then randomly selects an available host.
34
- If no hosts are available, it raises an exception.
35
- """
36
-
37
- # If no list of hosts to exclude is provided, initialize it as an empty list
38
- if exclude_hosts is None: # Check if exclude_hosts parameter was omitted or set to None
39
- exclude_hosts = [] # Initialize exclude_hosts to an empty list to avoid errors during filtering
40
-
41
- # Check if the session ID already has an assigned host in the mapping dictionary
42
- if session_id in mapping: # Verify if a host was previously assigned to this session
43
- assigned_host = mapping[session_id] # Retrieve the assigned host dictionary for this session
44
- # If the assigned host is not in the list of hosts to exclude, return it immediately
45
- if assigned_host["jarvis"] not in exclude_hosts: # Ensure assigned host is allowed for this request
46
- return assigned_host # Return the cached host assignment for session consistency
47
- else:
48
- # If the assigned host is excluded, remove the mapping to allow reassignment
49
- del mapping[session_id] # Delete the existing session-host mapping to find a new host
50
-
51
- # Get the current UTC time to compare against host busy status timestamps
52
- now = datetime.utcnow() # Capture current time to filter out hosts that are still busy
53
 
54
- # Create a list of hosts that are not currently busy and not in the exclude list
55
- available_hosts = [
56
- h for h in auth # Iterate over all hosts defined in the authentication configuration
57
- if h["jarvis"] not in busy or busy[h["jarvis"]] <= now # Include hosts not busy or whose busy time has expired
58
- if h["jarvis"] not in exclude_hosts # Exclude hosts specified in the exclude_hosts list
59
- ]
60
 
61
- # If no hosts are available after filtering, raise an exception to indicate resource exhaustion
62
- if not available_hosts: # Check if the filtered list of hosts is empty
63
- raise gr.Error("The server is currently busy. Please wait a moment or try again later", duration=5, title="INFO")
64
- # Inform user of service unavailability
65
-
66
- # Randomly select one host from the list of available hosts to distribute load evenly
67
- selected = random.choice(available_hosts) # Choose a host at random to avoid bias in host selection
68
-
69
- # Store the selected host in the mapping dictionary for future requests with the same session ID
70
- mapping[session_id] = selected # Cache the selected host to maintain session affinity
71
 
72
- # Mark the selected host as busy using the helper function to update its busy status
73
- mark(selected["jarvis"]) # Update the busy dictionary to indicate this host is now in use
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- # Return the selected host dictionary to the caller for use in processing the session
76
- return selected # Provide the caller with the assigned host details
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
+ import random # Import the random module to enable random selection of elements from a list, which helps in load balancing host assignments
7
+ from datetime import datetime # Import the datetime class to work with timestamps, particularly to check and compare current UTC time against host busy status
8
+ from typing import Dict, List # Import type hinting classes for dictionaries and lists to improve code readability and static analysis, though not explicitly used here
9
+ from config import auth # Import the 'auth' configuration, which is expected to be a list of host dictionaries containing credentials and identifiers for available hosts
10
+ from src.utils.helper import busy, mark # Import 'busy', a dictionary tracking host busy states with expiration timestamps, and 'mark', a function to update the busy status of hosts when assigned
 
11
 
12
+ # Initialize a global dictionary named 'mapping' that will maintain a persistent association between session IDs and their assigned hosts
13
+ mapping = {} # This dictionary stores session_id keys mapped to host dictionaries, ensuring consistent host allocation for repeated requests within the same session
14
 
15
+ # Define a function to get an available hosts for a given session
16
  def get_host(session_id: str, exclude_hosts: List[str] = None) -> dict:
17
  """
18
+ Obtain a host for the specified session ID, guaranteeing that the function never raises exceptions or returns None.
19
+ This function implements a robust mechanism to ensure a host is always returned by dynamically excluding busy or failed hosts,
20
+ and retrying until a suitable host becomes available.
21
 
22
  Args:
23
+ session_id (str): A unique string identifier representing the current user or process session requesting a host.
24
+ exclude_hosts (List[str], optional): An optional list of host identifiers to be excluded from selection, useful for avoiding problematic or previously failed hosts. Defaults to None.
25
 
26
  Returns:
27
+ dict: A dictionary representing the selected host from the 'auth' configuration, including its credentials and identifier.
28
 
29
+ Detailed Explanation:
30
+ The function first checks if the session already has an assigned host in the 'mapping' dictionary. If so, it verifies whether
31
+ the assigned host is neither excluded nor currently busy. If the assigned host is available, it refreshes its busy status to
32
+ extend its reservation and returns it immediately, ensuring session affinity.
33
 
34
+ If the assigned host is excluded or busy, the mapping is deleted to allow reassignment. The function then filters the list of
35
+ all hosts from 'auth' to exclude busy hosts, explicitly excluded hosts, and hosts already tried in the current function call.
36
+ From the filtered list, it randomly selects a host to distribute load evenly.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ If no suitable hosts are found, the function updates the set of tried hosts to include all currently busy and excluded hosts,
39
+ and clears the exclude list to retry all hosts again. This loop continues indefinitely until a host becomes available,
40
+ ensuring that the function never fails or returns None.
 
 
 
41
 
42
+ This design supports high availability and fault tolerance by dynamically adapting to host availability and avoiding deadlocks
43
+ or infinite retries on unavailable hosts.
44
+ """
 
 
 
 
 
 
 
45
 
46
+ # Initialize exclude_hosts as an empty list if no value is provided to avoid errors during host filtering
47
+ if exclude_hosts is None:
48
+ exclude_hosts = [] # Assign an empty list to exclude_hosts to safely perform membership checks and list operations later
49
+
50
+ # Create a set to track hosts that have been attempted and found unsuitable during this invocation of the function
51
+ tried_hosts = set() # Using a set for efficient membership testing and to prevent repeated attempts on the same hosts within this call
52
+
53
+ # Enter an infinite loop that will only exit when a valid host is found and returned, ensuring robustness and continuous operation
54
+ while True:
55
+ # Check if the current session ID already has a host assigned in the mapping dictionary
56
+ if session_id in mapping:
57
+ assigned_host = mapping[session_id] # Retrieve the previously assigned host dictionary for this session to maintain session consistency
58
+
59
+ # Determine if the assigned host is not in the exclude list and is either not busy or its busy period has expired
60
+ if (
61
+ assigned_host["jarvis"] not in exclude_hosts # Confirm the assigned host is not explicitly excluded for this request
62
+ and (
63
+ assigned_host["jarvis"] not in busy # Check if the host is not currently marked as busy
64
+ or busy[assigned_host["jarvis"]] <= datetime.utcnow() # Alternatively, check if the host's busy timestamp has expired, meaning it is free
65
+ )
66
+ ):
67
+ # Since the assigned host is available, update its busy timestamp to extend its reservation for this session
68
+ mark(assigned_host["jarvis"]) # Call the helper function to refresh the busy status, preventing other sessions from using it simultaneously
69
+ return assigned_host # Return the assigned host dictionary immediately to maintain session affinity and reduce latency
70
+
71
+ else:
72
+ # If the assigned host is either excluded or currently busy, remove the mapping to allow reassignment of a new host
73
+ del mapping[session_id] # Delete the session-to-host association to enable the selection of a different host in subsequent steps
74
+
75
+ # Capture the current UTC time once to use for filtering hosts based on their busy status
76
+ now = datetime.utcnow() # Store the current time to compare against busy timestamps for all hosts
77
+
78
+ # Generate a list of hosts that are eligible for selection by applying multiple filters:
79
+ # 1. Hosts not currently busy or whose busy period has expired
80
+ # 2. Hosts not included in the exclude_hosts list provided by the caller
81
+ # 3. Hosts not already attempted in this function call to avoid redundant retries
82
+ available_hosts = [
83
+ h for h in auth # Iterate over all hosts defined in the authentication configuration list
84
+ if h["jarvis"] not in busy or busy[h["jarvis"]] <= now # Include only hosts that are free or whose busy reservation has expired
85
+ if h["jarvis"] not in exclude_hosts # Exclude hosts explicitly marked to be avoided for this selection
86
+ if h["jarvis"] not in tried_hosts # Exclude hosts that have already been tried and failed during this function call to prevent infinite loops
87
+ ]
88
+
89
+ # If there are any hosts available after filtering, proceed to select one randomly
90
+ if available_hosts:
91
+ selected = random.choice(available_hosts) # Randomly pick one host from the available list to distribute load fairly among hosts
92
+
93
+ # Store the selected host in the global mapping dictionary to maintain session affinity for future requests with the same session ID
94
+ mapping[session_id] = selected # Cache the selected host so subsequent calls with this session ID return the same host
95
+
96
+ # Mark the selected host as busy by updating its busy timestamp, indicating it is currently reserved for this session
97
+ mark(selected["jarvis"]) # Update the busy dictionary to reflect that this host is now occupied, preventing concurrent use
98
+
99
+ # Return the selected host dictionary to the caller, completing the host assignment process for the session
100
+ return selected
101
 
102
+ else:
103
+ # If no hosts are available that have not already been tried, update the tried_hosts set to include all hosts currently busy or excluded
104
+ # This prevents immediate reattempts on these hosts in the next iteration, allowing time for busy hosts to become free
105
+ tried_hosts.update(
106
+ h["jarvis"] for h in auth # Iterate over all hosts in the auth list to identify those currently busy
107
+ if h["jarvis"] in busy and busy[h["jarvis"]] > now # Add hosts whose busy timestamp is still in the future, indicating they are occupied
108
+ )
109
+ tried_hosts.update(exclude_hosts) # Also add all explicitly excluded hosts to the tried_hosts set to avoid retrying them immediately
110
+
111
+ # Create a set of all host identifiers from the auth configuration to compare against tried_hosts
112
+ all_host_ids = {h["jarvis"] for h in auth} # Extract all host IDs to check if all hosts have been attempted
113
+
114
+ # If the set of tried hosts now includes every host in the configuration, clear the tried_hosts set to reset the retry mechanism
115
+ if tried_hosts >= all_host_ids:
116
+ tried_hosts.clear() # Clear the tried_hosts set to allow all hosts to be considered again, enabling retries after some time
117
+
118
+ # Clear the exclude_hosts list as well to allow all hosts to be eligible for selection in the next iteration
119
+ exclude_hosts.clear() # Reset the exclude list so that no hosts are excluded in the upcoming retry cycle, maximizing availability
src/utils/tools.py CHANGED
@@ -3,65 +3,85 @@
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
- import random # Import random module to enable random selection from a list
7
- import gradio as gr # Import the Gradio library to build interactive web interfaces for machine learning applications
8
- from datetime import datetime # Import datetime class to work with current UTC time
9
- from config import auth # Import authentication configuration, likely a list of host dictionaries with credentials
10
- from src.utils.helper import busy, mark # Import 'busy' dictionary and 'mark' function to track and update host busy status
11
 
 
12
  def initialize_tools():
13
  """
14
- Initialize and select available tools (endpoints) from the configured hosts.
15
 
16
- Returns:
17
- tuple: A tuple containing three elements:
18
- - tool_setup (str): The endpoint or configuration for the main tool setup.
19
- - image_tool (str): The endpoint URL for image generation services.
20
- - audio_tool (str): The endpoint URL for audio generation services.
21
 
22
- Raises:
23
- Exception: If no available hosts are found or if any required tool endpoint is missing.
 
 
 
 
 
 
 
24
 
25
- Explanation:
26
- This function filters the list of hosts from 'auth' to find those not currently busy or whose busy period has expired.
27
- It randomly selects one available host, marks it as busy for one hour,
28
- and retrieves the required tool endpoints ('done', 'image', 'audio') from the selected host's configuration.
29
- If any of these endpoints are missing, it raises an exception.
30
- Finally, it returns the three tool endpoints as a tuple.
31
- """
32
- # Get the current UTC time for busy status comparison
33
- now = datetime.utcnow()
34
 
35
- # Filter hosts that are either not marked busy or whose busy period has expired
36
- available = [
37
- item for item in auth
38
- if item["jarvis"] not in busy or busy[item["jarvis"]] <= now
39
- ]
 
 
 
 
40
 
41
- # Raise an exception if no hosts are currently available
42
- if not available:
43
- raise gr.Error("The server is currently busy. Please wait a moment or try again later", duration=5, title="INFO")
44
- # Inform user of service unavailability
 
 
45
 
46
- # Randomly select one host from the available list
47
- selected = random.choice(available)
 
 
 
 
 
 
48
 
49
- # Mark the selected host as busy for the next hour to prevent immediate reassignment
50
- mark(selected["jarvis"])
51
 
52
- # Retrieve the tool endpoints from the selected host's configuration dictionary
53
- tool_setup = selected.get("done") # Main tool setup endpoint or configuration
54
- image_tool = selected.get("image") # Image generation service endpoint
55
- audio_tool = selected.get("audio") # Audio generation service endpoint
56
- poll_token = selected.get("pol") # Pollinations AI API Token
 
 
 
 
 
57
 
58
- # Verify that all required tool endpoints are present, raise exception if any is missing
59
- if not tool_setup or not image_tool or not audio_tool or not poll_token:
60
- raise gr.Error("The server is currently busy. Please wait a moment or try again later", duration=5, title="INFO")
61
- # Inform user of service unavailability
 
 
 
 
62
 
63
- # Return the three tool endpoints as a tuple
64
- return tool_setup, image_tool, audio_tool, poll_token
 
 
 
65
 
66
- # Initialize the tools by selecting an available host and retrieving its endpoints
67
- tool_setup, image_tool, audio_tool, poll_token = initialize_tools()
 
 
3
  # SPDX-License-Identifier: Apache-2.0
4
  #
5
 
6
+ import random # Import the random module to enable functions for random selection and shuffling, which are essential for distributing workload evenly across multiple hosts and avoiding selection bias
7
+ from datetime import datetime, timedelta # Import datetime and timedelta classes to handle precise time calculations, which are crucial for managing host availability windows and expiration of busy statuses
8
+ from config import auth # Import the 'auth' configuration list, which contains dictionaries representing hosts with their respective credentials, endpoints, and tokens necessary for tool integration
9
+ from src.utils.helper import busy, mark # Import 'busy', a dictionary tracking the busy state and expiration timestamps of hosts, and 'mark', a utility function to update this dictionary to indicate when a host has been assigned and for how long
 
10
 
11
+ # Define a function to get available tools
12
  def initialize_tools():
13
  """
14
+ This function is designed to robustly and perpetually search for an available host from a predefined list of hosts, ensuring that the selected host is fully configured with all required service endpoints and tokens before returning.
15
 
16
+ It operates in an infinite loop to guarantee that it never returns a None value or raises an exception, thereby providing a reliable mechanism for host selection even in dynamic environments where host availability fluctuates frequently.
 
 
 
 
17
 
18
+ The detailed workflow is as follows:
19
+ - Obtain the current UTC timestamp to accurately evaluate the expiration of hosts' busy periods.
20
+ - Filter the list of configured hosts to include only those that are either not currently marked as busy or whose busy period has elapsed, thus ensuring only eligible hosts are considered for assignment.
21
+ - If no hosts meet the availability criteria, identify and remove expired busy entries from the tracking dictionary to free up hosts and retry immediately.
22
+ - Randomly shuffle the filtered list of available hosts to prevent selection bias and promote equitable distribution of workload.
23
+ - Iterate through the shuffled hosts, verifying that each host possesses all critical configuration elements: the main tool setup endpoint, image generation endpoint, audio generation endpoint, and Pollinations AI API token.
24
+ - Upon finding a host with complete and valid configurations, mark it as busy for a fixed duration (one hour) to prevent immediate reassignment and potential conflicts.
25
+ - Return a tuple containing the four key elements from the selected host, enabling downstream components to utilize these endpoints and tokens for their operations.
26
+ - If no suitable host is found after checking all available hosts, perform a cleanup of expired busy statuses and continue the search without delay.
27
 
28
+ This approach ensures continuous availability of a properly configured host, adapts dynamically to changing host states, and maintains system stability by preventing simultaneous conflicting assignments.
 
 
 
 
 
 
 
 
29
 
30
+ Returns:
31
+ tuple: A four-element tuple consisting of:
32
+ - tool_setup (str): The endpoint or configuration string for the primary tool setup service.
33
+ - image_tool (str): The URL endpoint for the image generation service.
34
+ - audio_tool (str): The URL endpoint for the audio generation service.
35
+ - poll_token (str): The API token string required for authenticating with Pollinations AI services.
36
+ """
37
+ while True: # Start an endless loop that only breaks when a fully valid and available host is successfully selected and returned
38
+ now = datetime.utcnow() # Capture the current coordinated universal time to accurately compare against host busy expiration timestamps
39
 
40
+ # Create a filtered list of hosts that are eligible for assignment
41
+ # Eligibility criteria include hosts not currently marked as busy or those whose busy period has expired, thereby allowing their reuse
42
+ available = [
43
+ item for item in auth # Iterate over each host configuration dictionary in the 'auth' list imported from the configuration module
44
+ if item["jarvis"] not in busy or busy[item["jarvis"]] <= now # Include the host if it is not present in the busy dictionary or if its busy timestamp is earlier than or equal to the current time, indicating availability
45
+ ]
46
 
47
+ # Check if the filtered list of available hosts is empty, which means all hosts are currently busy with unexpired busy periods
48
+ if not available:
49
+ # Identify all hosts in the busy dictionary whose busy periods have expired by comparing their timestamps to the current time
50
+ expired_hosts = [host for host in busy if busy[host] <= now] # Generate a list of host identifiers whose busy status has expired and are thus candidates for cleanup
51
+ for host in expired_hosts: # Iterate over each expired host to perform cleanup operations
52
+ del busy[host] # Remove the host's busy status entry from the dictionary, effectively marking it as available again for assignment
53
+ # After cleaning up expired busy statuses, continue the loop to reattempt host selection immediately, ensuring responsiveness and minimal delay in availability checks
54
+ continue
55
 
56
+ # Randomize the order of the available hosts list to prevent any selection bias and promote fair workload distribution among hosts
57
+ random.shuffle(available)
58
 
59
+ # Iterate through each host in the shuffled list to find one that has all the required endpoints and tokens properly configured
60
+ for selected in available:
61
+ # Extract the main tool setup endpoint from the selected host's configuration dictionary using the key 'done'
62
+ tool_setup = selected.get("done")
63
+ # Extract the image generation service endpoint URL using the key 'image'
64
+ image_tool = selected.get("image")
65
+ # Extract the audio generation service endpoint URL using the key 'audio'
66
+ audio_tool = selected.get("audio")
67
+ # Extract the Pollinations AI API token string using the key 'pol'
68
+ poll_token = selected.get("pol")
69
 
70
+ # Verify that all four critical configuration values are present and non-empty, ensuring the host is fully capable of handling the required services
71
+ if tool_setup and image_tool and audio_tool and poll_token:
72
+ # Mark the selected host as busy for the next hour by invoking the 'mark' function with the host's unique identifier
73
+ # This prevents the same host from being assigned again immediately, allowing for load balancing and avoiding conflicts
74
+ mark(selected["jarvis"])
75
+ # Return a tuple containing all four endpoints and the token, signaling successful host selection and configuration retrieval
76
+ return tool_setup, image_tool, audio_tool, poll_token
77
+ # If any required endpoint or token is missing, skip this host and continue checking the next one in the list
78
 
79
+ # If none of the available hosts had all required endpoints and tokens, perform cleanup of expired busy statuses again before retrying
80
+ expired_hosts = [host for host in busy if busy[host] <= now] # Identify hosts whose busy times have expired and are eligible to be freed up
81
+ for host in expired_hosts:
82
+ del busy[host] # Remove expired busy entries to free up hosts for reassignment
83
+ # Continue the loop to retry host selection immediately, maintaining continuous operation without delay
84
 
85
+ # Execute the host initialization process by calling the function to obtain an available host's endpoints and tokens
86
+ tool_setup, image_tool, audio_tool, poll_token = initialize_tools()
87
+ # This call blocks until a suitable host is found and returns the necessary configuration for downstream tool usage