Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Python SDK Migration Guide

Norman Bukingolts edited this page Oct 28, 2024 · 8 revisions

Python SDK Migration Guide

A guide for developers migrating to the Fern-generated Python SDK (version 0.7.0 and above).

Hume’s newest Python SDK refactors the core client architecture, separating functionality into distinct modules for specific APIs (e.g., the Expression Measurement API and the Empathic Voice Interface API).

Version 0.7.0 introduces the following features:

  • Explicit types
  • Better support for asynchronous operations
  • More granular client configuration
  • Continued support for legacy SDK implementations
  • Support for Python version 3.12 with Expression Measurement API namespace methods

This guide will help you adapt your code to the new SDK structure with practical examples and explanations of the key differences.


Compatibility

Below is a matrix showing the compatibility of the Hume Python SDK across various Python versions and operating systems.

Python VersionOperating System
Empathic Voice Interface3.9, 3.10, 3.11macOS, Linux
Expression Measurement3.9, 3.10, 3.11, 3.12macOS, Linux, Windows

For the Empathic Voice Interface, Python versions 3.9 through 3.11 are supported on macOS and Linux.

For Expression Measurement, Python versions 3.9 through 3.12 are supported on macOS, Linux, and Windows.


Support for the legacy SDK

The legacy SDK is entirely contained within the new SDK’s src/hume/legacy folder in order to ensure smooth transition to the new features. To preserve your code’s current functionality, follow these steps:

  1. Run pip install “hume[legacy]" to install the legacy package extra.
    1. If you are using EVI’s microphone utilities, run pip install “hume[microphone]” to install the microphone extra.
  2. Change your import statements to from hume.legacy instead of from hume.

Example

fromhume.legacyimportHumeVoiceClient, VoiceConfigclient=HumeVoiceClient("<YOUR_API_KEY>") config=client.empathic_voice.configs.get_config_version( id="id", version=1 )

Primary change: synchronous and asynchronous base clients

Instead of using HumeBatchClient, HumeStreamClient, or HumeVoiceClient, now use AsyncHumeClient - the new asynchronous base client.

This client is authenticated with your Hume API key and provides access to the Expression Measurement API and Empathic Voice Interface API as namespaces. If you're not using async, the synchronous HumeClient is available, but we recommend defaulting to AsyncHumeClient for most use cases.

Each API is namespaced accordingly:

fromhume.clientimportAsyncHumeClient# base synchronous client client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Expression Measurement (Batch) client.expression_measurement.batch# Expression Measurement (Streaming) client.expression_measurement.streaming# Empathic Voice Interface client.empathic_voice.

Importantly, invoking asynchronous functionality (e.g., instantiating an EVI WebSocket connection) when using a synchronous client (i.e., HumeClient) is disallowed behavior and causes an error. On the other hand, invoking synchronous behavior from an asynchronous client is supported, however each method must be awaited.

fromhume.clientimportHumeClient, AsyncHumeClient# INVALID: using a synchronous client for asynchronous behavior client=HumeClient(api_key=<HUME_API_KEY>)
# Using the asynchronous connect method with a sync client will cause an error asyncwithclient.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for asynchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Using the async connect method with an async client will work properly asyncwithasync_client.empathic_voice.chat.connect() assocket: # ...# VALID: using an asynchronous client for synchronous behavior async_client=AsyncHumeClient(api_key=<HUME_API_KEY>) # Using the configs.list_configs() method with an async client print(awaitclient.empathic_voice.configs.list_configs())

Using the Empathic Voice Interface (EVI)

First, identify what operations you would like to perform.

  • For tasks such as creating a config, listing the tools you have available, and more, we recommend using the Hume Portal because of its comprehensive user interface.
  • For chatting with EVI (i.e., accessing the chat endpoint), it is required to use the asynchronous Hume client.
  • If you need to interact with configurations, tools, or other items programmatically, it is recommended to use the asynchronous Hume client - but possible to use the synchronous client if needed.

Then, authenticate the client and proceed with your desired functionality.

Types introduced for EVI

The EVI WebSocket connection is now configurable using an explicit type: ChatConnectOptions. This object must be passed into the method used to initialize the connection.

Examples: New SDK, Empathic Voice Interface

Using EVI from a synchronous context (e.g., listing your configs)

fromhume.clientimportHumeClient# authenticate the synchronous client client=HumeClient(api_key=<HUME_API_KEY>) # list your configs client.empathic_voice.configs.list_configs()

Using EVI from an asynchronous context (e.g., starting a chat)

It is now possible to fully manage the WebSocket events with your EVI integration, meaning you can define custom behavior when the WebSocket is opened, closed, receives a message, or receives an error. Use the new asynchronous client’s connect_with_callbacks function to do so, and reference the SubscribeEvent message type within your on_message callback function.

fromhume.clientimportAsyncHumeClientfromhume.empathic_voice.chat.socket_clientimportChatConnectOptionsasyncdefmain() ->None: # Initialize the asynchronous client, authenticating with your API key client=AsyncHumeClient(api_key=<HUME_API_KEY>)
# Define options for the WebSocket connection, such as an EVI config id and a secret key for token authentication options=ChatConnectOptions(config_id=<HUME_CONFIG_ID>, secret_key=<HUME_SECRET_KEY>)
# Open the WebSocket connection with the configuration options and the interface's handlers asyncwithclient.empathic_voice.chat.connect_with_callbacks( options=options, on_open=<customon_openfunction>, on_message=<customon_messagefunction>, on_close=<customon_closefunction>, on_error=<customon_errorfunction> ) assocket: # ...if__name__=="__main__": asyncio.run(main())

Example on_message handler

asyncdefon_message(message: SubscribeEvent): """Callback function to handle a WebSocket message event. Args:  data (SubscribeEvent): This represents any type of message that is received through the EVI WebSocket, formatted in JSON. See the full list of messages in the API Reference [here](https://dev.hume.ai/reference/empathic-voice-interface-evi/chat/chat#receive).  """# Create an empty dictionary to store expression inference scores scores= {}
ifmessage.type=="chat_metadata": message_type=message.type.upper() chat_id=message.chat_idchat_group_id=message.chat_group_idtext=f"<{message_type}> Chat ID: {chat_id}, Chat Group ID: {chat_group_id}"elifmessage.typein ["user_message", "assistant_message"]: role=message.message.role.upper() message_text=message.message.contenttext=f"{role}: {message_text}"ifmessage.from_textisFalse: scores=dict(message.models.prosody.scores) elifmessage.type=="audio_output": message_str: str=message.datamessage_bytes=base64.b64decode(message_str.encode("utf-8")) awaitself.byte_strs.put(message_bytes) returnelifmessage.type=="error": error_message: str=message.messageerror_code: str=message.coderaiseApiError(f"Error ({error_code}): {error_message}") # ApiError is also an imported type else: message_type=message.type.upper() text=f"<{message_type}>"print(text)

Example: Legacy SDK, Empathic Voice Interface

fromhumeimportHumeVoiceClient, MicrophoneInterfaceimportasyncioasyncdefmain() ->None: # Connect and authenticate with Hume client=HumeVoiceClient(<HUME_API_KEY>)
# Start streaming EVI over your device's microphone and speakers asyncwithclient.connect() assocket: awaitMicrophoneInterface.start(socket)
if__name__=="__main__": asyncio.run(main())

Using the Expression Measurement API (Batch)

Instantiate the asynchronous client, configure the job with a Models object, and submit your media URLs for processing. Once submitted and the job is awaited to completion, predictions may be retrieved based on the job ID.

  • The await_complete() method on a job has been removed; developers will need to implement a mechanism such as polling the job’s status to await the completion of the job.
  • The download_predictions() method on a job has also been removed; developers will need to implement an HTTP call to the API, parse the results, and export them to a file.

Prior to the update, when you started a job and passed in the job configuration, it would be the case that the start_inference_job would accept the model configs as an array. Now, this is all contained within a typed models object.

Types introduced for Batch

Starting an inference job now involves defining configuration options using explicit types for each model. For example, a Face object corresponds to the model’s configuration options. Configurations are passed into a Models object, which in turn is passed into the start_inference_job method. Similar strict typing exists with other batch methods.

Example: New SDK, Expression Measurement - Hosted File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=<YOUR_API_KEY>)
# Define the URL(s) of the files you would like to analyze job_urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job( urls=job_urls, models=models_chosen )
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Example: New SDK, Expression Measurement - Local File

fromhumeimportAsyncHumeClientfromhume.expression_measurement.batchimportFace, Modelsfromhume.expression_measurement.batch.typesimportInferenceBaseRequestasyncdefmain(): # Initialize an authenticated client client=AsyncHumeClient(api_key=HUME_API_KEY)
# Define the filepath(s) of the file(s) you would like to analyze local_filepaths= [open("faces.zip", mode="rb")]
# Create configurations for each model you would like to use (blank = default) face_config=Face()
# Create a Models object models_chosen=Models(face=face_config) # Create a stringified object containing the configuration stringified_configs=InferenceBaseRequest(models=models_chosen)
# Start an inference job and print the job_id job_id=awaitclient.expression_measurement.batch.start_inference_job_from_local_file( json=stringified_configs, file=local_filepaths)
# Await the completion of the inference job awaitpoll_for_completion(client, job_id, timeout=120)
# After the job is over, access its predictions job_predictions=awaitclient.expression_measurement.batch.get_job_predictions( id=job_id )
if__name__=="__main__": asyncio.run(main())

Awaiting job completion

Below is an example implementation of helper methods which incorporate polling the job’s status for completion with exponential backoff.

asyncdefpoll_for_completion(client: AsyncHumeClient, job_id, timeout=120): """  Polls for the completion of a job with a specified timeout (in seconds). Uses asyncio.wait_for to enforce a maximum waiting time.  """try: # Wait for the job to complete or until the timeout is reached awaitasyncio.wait_for(poll_until_complete(client, job_id), timeout=timeout) exceptasyncio.TimeoutError: # Notify if the polling operation has timed out print(f"Polling timed out after {timeout} seconds.")
asyncdefpoll_until_complete(client: AsyncHumeClient, job_id): """  Continuously polls the job status until it is completed, failed, or an unexpected status is encountered. Implements exponential backoff to reduce the frequency of requests over time.  """delay=1# Start with a 1-second delaywhileTrue: # Wait for the specified delay before making the next status check awaitasyncio.sleep(delay)
# Retrieve the current job details job_details=awaitclient.expression_measurement.batch.get_job_details(job_id) status=job_details.state.statusifstatus=="COMPLETED": # Job has completed successfully print("\nJob completed successfully:") breakelifstatus=="FAILED": # Job has failed print("\nJob failed:") break# Increase the delay exponentially, maxing out at 16 seconds delay=min(delay*2, 16)

Downloading job artifacts

The SDK may be used to download the job’s artifacts.

Download the job's artifacts

withopen("artifacts.zip", "wb") asf: asyncfornew_bytesinclient.expression_measurement.batch.get_job_artifacts(job_id): f.write(new_bytes)

Downloading job predictions

The API must be called directly to download the job’s predictions.

If using the code below, ensure you replace <YOUR_JOB_ID> and <YOUR_API_KEY> below with the respective correct values.

importrequestsimportjson# Define the URL and headers url="https://api.hume.ai/v0/batch/jobs/<YOUR_JOB_ID>/predictions"headers= { "X-Hume-Api-Key": "<YOUR_API_KEY>" }
# Make the GET request response=requests.get(url, headers=headers)
# Check if the request was successful ifresponse.status_code==200: # Parse the JSON response data=response.json() # Write the JSON data to a file withopen("predictions.json", "w") asfile: json.dump(data, file, indent=2) print("Response has been written to 'predictions.json'.") else: print(f"Failed to fetch data. Status code: {response.status_code}") print(response.text)

Example: Legacy SDK, Expression Measurement

fromhumeimportHumeBatchClientfromhume.models.configimportFaceConfigfromhume.models.configimportProsodyConfigclient=HumeBatchClient(<HUME_API_KEY>) urls= ["https://hume-tutorials.s3.amazonaws.com/faces.zip"]
face_config=FaceConfig() prosody_config=ProsodyConfig()
job=client.submit_job(urls, [face_config, prosody_config]) print(job) print("Running...")
result=job.await_complete() job_predictions=client.get_job_predictions(job_id=job.id)

Using the Expression Measurement API (Streaming)

First, retrieve the samples you will use. Then, instantiate the asynchronous client and configure the WebSocket with a Config object containing the model(s) you would like to use. After you connect to the WebSocket, predictions may be retrieved.

Types introduced for Streaming

Connecting to the WebSocket now uses the explicit type StreamConnectOptions. These options accept the Config object, which contains the configurations for the expression measurement models you wish to use. These configurations are unique to each model and need importing as well, such as with StreamLanguage.

Example: New SDK, Expression Measurement

importasynciofromhumeimportAsyncHumeClientfromhume.expression_measurement.streamimportConfigfromhume.expression_measurement.stream.socket_clientimportStreamConnectOptionsfromhume.expression_measurement.stream.typesimportStreamLanguagesamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=AsyncHumeClient(api_key="<YOUR_API_KEY>")
model_config=Config(language=StreamLanguage())
stream_options=StreamConnectOptions(config=model_config)
asyncwithclient.expression_measurement.stream.connect(options=stream_options) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) print(result.language.predictions[0]['emotions'])
if__name__=="__main__": asyncio.run(main())

Example: Legacy SDK, Expression Measurement

importasynciofromhumeimportHumeStreamClientfromhume.models.configimportLanguageConfigsamples= [ "Mary had a little lamb,", "Its fleece was white as snow.""Everywhere the child went,""The little lamb was sure to go." ]
asyncdefmain(): client=HumeStreamClient("<YOUR API KEY>") config=LanguageConfig() asyncwithclient.connect([config]) assocket: forsampleinsamples: result=awaitsocket.send_text(sample) emotions=result["language"]["predictions"][0]["emotions"] print(emotions)
if__name__=="__main__": asyncio.run(main())