Process queued text jobs locally and with Sherlock on CloudFerro Cloud
A website accepting a request does not always need to wait for the work to finish. It can place a job in Message Broker and let a separate worker process it later. This is useful for translation, document processing, and other tasks whose processing time varies.
The first example converts a queued sentence to uppercase using Python. The second sends a translation job through the same queue and calls Sherlock from the worker. Both save the result to a JSON file before acknowledging the message.
Prerequisites
No. 1 A working connection to Message Broker
Complete Connect an application and send a first message on CloudFerro Cloud. Keep its Python virtual environment, AMQPS hostname, port, RabbitMQ username, and vhost settings. The computer running this example must be admitted by Allowed IP Ranges.
No. 2 Permission to use a demonstration queue
Use a test vhost or agree on an unused queue name with the broker owner. The example creates a durable classic queue named docs-text-jobs; the account needs permission to declare that queue, publish through the default exchange, and read messages. Follow Manage RabbitMQ queues and access on CloudFerro Cloud to prepare the account and vhost. Use one worker for this exercise.
No. 3 The example program
Download text_jobs.py into your working folder. It uses Python 3, pika 1.3.2, and Requests. The commands below use the Bash terminal and virtual environment prepared in the connection guide.
No. 4 Sherlock access for the translation example only
Prepare a project API key using Authenticate to Sherlock AI with API keys on CloudFerro Cloud. Select an available chat model using Models endpoint on CloudFerro Cloud. The worker needs outbound HTTPS access to the Sherlock API. The uppercase example does not use Sherlock or an API key.
Prepare the terminal
Activate the virtual environment and install the two libraries:
source .venv/bin/activate
python -m pip install pika==1.3.2 requests
Use your broker’s values for these variables. The vhost / is valid only if it exists and the account can use it:
export RABBITMQ_HOST='broker.example.invalid'
export RABBITMQ_PORT='5671'
export RABBITMQ_USER='YOUR_RABBITMQ_USER'
export RABBITMQ_VHOST='/'
Each command prompts for the RabbitMQ password without displaying it. The program verifies the broker’s TLS certificate and hostname.
Example 1: send work while the worker is stopped
Publish a short text job:
python text_jobs.py publish uppercase --text 'The next satellite pass starts at 09:30 UTC.'The program prints Queued: followed by a job identifier and exits. It waits for a publisher confirmation from RabbitMQ, but it does not process the text.
Open Queues and Streams in the RabbitMQ Management UI. Select your vhost and find docs-text-jobs. Before starting the worker, the queue should show one Ready message if it was previously empty.
Start the worker in the same terminal:
python text_jobs.py workIt fetches one job, writes its result under results/, acknowledges the delivery, and exits. A successful run prints Saved and acknowledged: followed by the JSON filename. If no job arrives within 30 seconds, it exits with a message saying how many it completed.
Open the result file whose name matches the printed job identifier. Its result field contains:
THE NEXT SATELLITE PASS STARTS AT 09:30 UTC.
Refresh the queue view. The ready and unacknowledged counts should both return to zero when no other jobs are present. The queue itself remains available for the next job.
You can also publish and process from different computers. Copy the program and set the connection variables on both; each computer needs network access to the broker. Results are saved on the computer running the worker.
This demonstration fetches messages with basic_get, so it does not create a persistent consumer subscription. The console’s consumer count can remain zero even while the worker is running. A continuously running service normally uses a consumer subscription instead.
Example 2: translate queued text with Sherlock
The message contains the source text and target language. The worker holds the Sherlock API key, calls the chat completion endpoint, and saves the translated text. Message Broker stores and delivers the job; your Python program performs the integration.
In the worker terminal, read the API key without placing it in command history, then export it for Python:
read -rsp 'Sherlock API key: ' SHERLOCK_API_KEY printf '\n' export SHERLOCK_API_KEY export SHERLOCK_MODEL='YOUR_AVAILABLE_CHAT_MODEL_ID'
Replace YOUR_AVAILABLE_CHAT_MODEL_ID with the full model identifier returned for your project. Keep the key out of the script, messages, and result files.
Publish a translation request:
python text_jobs.py publish translate --language Polish --text 'The next satellite pass starts at 09:30 UTC.'The publisher needs RabbitMQ access. It does not call Sherlock and does not need the Sherlock API key when it runs in a separate terminal or on another computer.
Run the worker:
python text_jobs.py workInspect result in the saved JSON file. It should contain the sentence translated into Polish; wording can vary by model. Check that the time, timezone, and meaning were preserved before using the translation.
To request several target languages, publish one job for each language, then process that number of jobs:
python text_jobs.py publish translate --language German --text 'The next satellite pass starts at 09:30 UTC.' python text_jobs.py publish translate --language French --text 'The next satellite pass starts at 09:30 UTC.' python text_jobs.py work --count 2
Each job creates a separate result file and makes a separate Sherlock request. Review consumption in Monitor Sherlock consumption and events on CloudFerro Cloud.
How delivery and failures are handled
The queue is durable and the publisher marks messages as persistent. Publisher confirms report acceptance by the broker; the worker acknowledges only after saving a complete result. The example uses a classic queue for compatibility with a single-instance broker. A classic queue is not replicated merely because the broker has three replicas; choose and configure an appropriate queue type for a production application.
While Sherlock processes a request, a background thread handles the HTTP call and the main thread keeps the RabbitMQ connection active. Slow translation therefore does not deliberately block heartbeat processing. All RabbitMQ operations stay on the main thread.
If processing fails, the program closes without acknowledging that delivery. RabbitMQ can then deliver it again. Read the error and correct the cause before restarting the worker: a missing API key, rejected model, incomplete response, or unwritable results folder needs attention rather than repeated retries. The program does not automatically retry Sherlock requests.
A completed result is identified by the job ID and a fingerprint of its input. With one worker and the same results directory, a redelivered job that already has a matching result is acknowledged without repeating the work. This does not guarantee exactly-once execution: a crash after the API request but before the file is saved can repeat the request and incur additional usage. Running the publish command again also creates a new job ID.
The files are a simple demonstration result store. Before adding multiple workers, use a shared result store with concurrency control and a defined retry policy. Keep input files in Object Storage and send object references for larger jobs; this example limits text to 10,000 characters and messages to 64 KiB. Its 2,048-token output limit may also be too small for longer translations.
Use the example with other products
Virtual machines: run the publisher in a web application and keep the worker on a VM. Queueing lets the application accept requests while the worker is busy or temporarily stopped.
Managed Notebooks: publish a small batch from a notebook terminal and inspect the returned JSON during development. A notebook kernel or terminal must remain running if it also hosts the worker.
Workflows: have a scheduled Airflow task publish jobs after preparing source data. Store the resulting job IDs if a later stage must check completion; successful publishing alone does not mean translation has finished.
Managed Kubernetes and Container Registry: package the worker as a container, store the image in the registry, and run it in Kubernetes with credentials supplied as secrets. A production consumer and shared result store are needed before scaling to several replicas.
Functions: a RabbitMQ trigger can invoke function code when work arrives. Move the processing logic into the function handler and configure the trigger; the command-line polling loop in this download is not a Functions handler.
These are application designs you can build using each product’s access settings. Use Message Broker with other products on CloudFerro Cloud links the relevant setup paths and explains the separate credentials and regional requirements.
Finish the exercise
After all demonstration jobs have completed, check that docs-text-jobs has no ready or unacknowledged messages and that no application still uses it. You can then delete that demonstration queue in the RabbitMQ console. Keep any result files you need and remove the key from the worker’s environment:
unset SHERLOCK_API_KEY
The full implementation is in text_jobs.py. For delivery semantics, see RabbitMQ’s publisher confirms and consumer acknowledgements. For API request fields, continue with Sherlock chat completion endpoint on CloudFerro Cloud.