From f8031a0518c33f90c57b4cba6ff1b8d79d784d09 Mon Sep 17 00:00:00 2001 From: "attaboykurt.yang@gmail.com" Date: Tue, 2 Sep 2025 21:45:21 +0000 Subject: [PATCH] feat(api_server): Add OpenAI-compatible API server for MaxText models This commit introduces a fully-featured, OpenAI-compatible RESTful API server for serving MaxText models. The server is built with FastAPI, supports multi-host inference on TPUs, and is designed for both interactive use and large-scale benchmarking. Key features and additions: 1. **Core Server Implementation:** - Adds `maxtext_server.py`, a FastAPI application that serves `/v1/completions` and `/v1/chat/completions` endpoints. - Implements dynamic request batching to efficiently utilize underlying hardware. - Uses `maxtext_generator.py` to encapsulate the MaxText inference engine, handling model loading, tokenization, and the generation loop. - Includes Pydantic models in `server_models.py` for robust, OpenAI-compliant request and response validation. 2. **Deployment and Utilities:** - Provides `start_server.sh` to simplify launching the server from the project root. - Adds `port_forward_xpk.sh`, a utility script to automatically find and connect to a server running on a GKE cluster via `xpk`, supporting custom namespaces. - Isolates server-specific dependencies in `benchmarks/api_server/requirements.txt` (`uvicorn`, `fastapi`, `openai-harmony`). 3. **Comprehensive Documentation:** - A new `README.md` in the `api_server` directory offers a complete guide covering: - Installation and environment setup. - Launching the server in both single-pod and multi-pod GKE environments. - Detailed examples for interacting with the API using `curl` and the `openai` Python client. - Step-by-step instructions for running benchmarks with `lm-evaluation-harness` and `evalchemy` for both log-likelihood and generative tasks. --- benchmarks/api_server/README.md | 317 +++++++++ benchmarks/api_server/images/mmlu_example.png | Bin 0 -> 63636 bytes .../api_server/images/server-request-logs.png | Bin 0 -> 71899 bytes .../images/single-host-server-startup.png | Bin 0 -> 68358 bytes .../api_server/launch_gke_server.sh.template | 78 +++ benchmarks/api_server/maxtext_generator.py | 634 ++++++++++++++++++ benchmarks/api_server/maxtext_server.py | 431 ++++++++++++ benchmarks/api_server/port_forward_xpk.sh | 104 +++ benchmarks/api_server/requirements.txt | 4 + benchmarks/api_server/server_models.py | 206 ++++++ benchmarks/api_server/server_utils.py | 300 +++++++++ benchmarks/api_server/start_server.sh | 60 ++ 12 files changed, 2134 insertions(+) create mode 100644 benchmarks/api_server/README.md create mode 100644 benchmarks/api_server/images/mmlu_example.png create mode 100644 benchmarks/api_server/images/server-request-logs.png create mode 100644 benchmarks/api_server/images/single-host-server-startup.png create mode 100644 benchmarks/api_server/launch_gke_server.sh.template create mode 100644 benchmarks/api_server/maxtext_generator.py create mode 100644 benchmarks/api_server/maxtext_server.py create mode 100644 benchmarks/api_server/port_forward_xpk.sh create mode 100644 benchmarks/api_server/requirements.txt create mode 100644 benchmarks/api_server/server_models.py create mode 100644 benchmarks/api_server/server_utils.py create mode 100644 benchmarks/api_server/start_server.sh diff --git a/benchmarks/api_server/README.md b/benchmarks/api_server/README.md new file mode 100644 index 0000000000..4fd0f31c69 --- /dev/null +++ b/benchmarks/api_server/README.md @@ -0,0 +1,317 @@ +# MaxText API Server + +This directory contains an OpenAI-compatible API server for serving MaxText models, enabling benchmarks with evaluation frameworks like lm-eval-harness and evalchemy. It uses [FastAPI](https://fastapi.tiangolo.com/) as the web framework and can be deployed on a single machine or a multi-host GKE cluster. + +## Table of Contents +- [Installation](#installation) +- [Environment Variables](#environment-variables) +- [Launching the Server (Single-Host)](#launching-the-server-single-pod) +- [Deploying on a GKE Cluster (Multi-Host)](#deploying-on-a-gke-cluster-multi-host) +- [Interacting with the Server](#interacting-with-the-server) +- [Benchmarking with Evaluation Frameworks](#benchmarking-with-evaluation-frameworks) + + +## Installation + +The server has a few additional dependencies beyond the core MaxText requirements. Install them using the provided `requirements.txt` file: + +```bash +pip install -r benchmarks/api_server/requirements.txt +``` + +## Environment Variables + +Before launching the server, you may need to set the following environment variable: + +- `HF_TOKEN`: Your Hugging Face access token. This is required if the model's tokenizer is hosted on the Hugging Face Hub and is not public. + +```bash +export HF_TOKEN= +``` + +## Launching the Server (Single-Host) + +The primary way to launch the API server is by using the `start_server.sh` script. This script ensures that the server is run from the project's root directory, which is necessary for the Python interpreter to find all the required modules. + +The script takes the path to a base configuration file (e.g., `MaxText/configs/base.yml`) followed by any number of model-specific configuration overrides. + +### Benchmarking Configuration + +To use this server for benchmarking with frameworks like `lm-eval-harness` or `evalchemy`, you **must** include the following two arguments in your launch command: + +- `tokenizer_type="huggingface"`: Ensures the tokenizer is compatible with the evaluation harness. +- `return_log_prob=True`: Enables the log probability calculations required for many standard evaluation metrics. + +### Command Structure + +```bash +bash benchmarks/api_server/start_server.sh /path/to/base.yml [arg1=value1] [arg2=value2] ... +``` + +### Example + +Here is an example of how to launch the server with a `qwen3-30b-a3b` model, configured for benchmarking. This example is configured for a TPU v5p-8, which has 4 chips. + +```bash +# Make sure you are in the root directory of the maxtext project. + +bash benchmarks/api_server/start_server.sh \ + MaxText/configs/base.yml \ + model_name="qwen3-30b-a3b" \ + tokenizer_path="Qwen/Qwen3-30B-A3B-Thinking-2507" \ + load_parameters_path="" \ + per_device_batch_size=4 \ + ici_tensor_parallelism=4 \ + max_prefill_predict_length=1024 \ + max_target_length=2048 \ + async_checkpointing=false \ + scan_layers=false \ + attention="dot_product" \ + tokenizer_type="huggingface" \ + return_log_prob=True +``` + +Once the server starts successfully, you will see a confirmation message from Uvicorn: + +Single-Host Server Startup + +``` +INFO: RANK 0: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) +``` + +The server is now ready to accept requests on port 8000. + +## Deploying on a GKE Cluster (Multi-Host) + +For large models that require a multi-host TPU setup, you can deploy the server using the [xpk (Kubernetes Pod Executor) tool](https://github.com/AI-Hypercomputer/xpk). The recommended approach is to create a single submission script to configure and launch the workload. + + +### 1. Create a Job Submission Script + +Create a new bash script (e.g., `launch_gke_server.sh`) to hold your configuration and `xpk` command. This makes launching jobs repeatable and easy to modify. + +For your convenience, the script below is also available as a template file at `benchmarks/api_server/launch_gke_server.sh.template`. + +Inside this script, you will define the server's startup command and your cluster configuration. Before running the script, define the placeholders at the top of the file. Placeholders are enclosed in angle brackets (e.g., ``). + +```bash +#!/bin/bash +set -e + +# ============================================================================== +# 1. User-Configurable Variables +# ============================================================================== + +# -- GKE Cluster Configuration -- +# (, , ) +export CLUSTER="" +export DEVICE_TYPE="v5p-16" +export PROJECT="" +export ZONE="" + +# -- XPK Workload Configuration -- +# (, ) +export RUNNAME="my-server-$(date +%Y-%m-%d-%H-%M-%S)" +export DOCKER_IMAGE="gcr.io/tpu-prod-env-multipod/maxtext_jax_nightly:" +export HF_TOKEN="" # Optional: if your tokenizer is private + +# -- Model Configuration -- +# IMPORTANT: Replace these with your model's details. +# (, , ) +export MODEL_NAME="qwen3-30b-a3b" +export TOKENIZER_PATH="Qwen/Qwen3-30B-A3B-Thinking-2507" +export LOAD_PARAMETERS_PATH="" +export PER_DEVICE_BATCH_SIZE=4 +# Parallelism settings should match the number of chips on your device. +# For a v5p-16 (8 chips), the product of parallelism values should be 8. +export ICI_TENSOR_PARALLELISM=4 +export ICI_EXPERT_PARALLELISM=2 + +# ============================================================================== +# 2. Define the Command to Run on the Cluster +# ============================================================================== +# This command installs dependencies and then starts the server. +CMD="export HF_TOKEN=${HF_TOKEN} && \ + pip install --upgrade pip && \ + pip install -r benchmarks/api_server/requirements.txt && \ + bash benchmarks/api_server/start_server.sh \ + MaxText/configs/base.yml \ + model_name="${MODEL_NAME}" \ + tokenizer_path="${TOKENIZER_PATH}" \ + load_parameters_path="${LOAD_PARAMETERS_PATH}" \ + per_device_batch_size=${PER_DEVICE_BATCH_SIZE} \ + ici_tensor_parallelism=${ICI_TENSOR_PARALLELISM} \ + ici_expert_parallelism=${ICI_EXPERT_PARALLELISM} \ + tokenizer_type=\"huggingface\" \ + return_log_prob=True" + + +# ============================================================================== +# 3. Launch the Workload +# ============================================================================== +echo "Launching workload ${RUNNAME}..." +xpk workload create --workload "${RUNNAME}" \ + --base-docker-image "${DOCKER_IMAGE}" \ + --command "${CMD}" \ + --num-slices=1 \ + --cluster "${CLUSTER}" --device-type "${DEVICE_TYPE}" --project "${PROJECT}" --zone "${ZONE}" + +echo "Workload ${RUNNAME} created." +echo "Use the following command to connect:" +echo "bash benchmarks/api_server/port_forward_xpk.sh job_name=${RUNNAME} project=${PROJECT} zone=${ZONE} cluster=${CLUSTER}" +``` + +### 2. Launch the Workload + +Make the script executable and run it: + +```bash +chmod +x launch_gke_server.sh +./launch_gke_server.sh +``` + +### 3. Connect to the Server + +The API server only runs on the first host/worker (rank 0 on GPU) of the workload. To connect to it, use the `port_forward_xpk.sh` script as instructed in the output of your launch script. + +```bash +bash benchmarks/api_server/port_forward_xpk.sh \ + job_name= \ + project= \ + zone= \ + cluster= +``` + +The script will automatically find the correct pod and establish the port-forward connection. Your server is now accessible at `http://localhost:8000`. + +## Interacting with the Server + +Once the server is running (either locally or connected via port-forwarding), you can interact with it using any standard HTTP client. The `model` field in the request body can be set to any string; it is used for identification purposes but does not change which model is being served. + +### Using `curl` + +#### Completions API + +The `/v1/completions` endpoint is suitable for simple prompt-response interactions. + +```bash +curl -X POST http://localhost:8000/v1/completions \ +-H "Content-Type: application/json" \ +-d + "{ + "model": "", + "prompt": "The capital of France is", + "max_tokens": 50, + "temperature": 0.7 +}" +``` + +#### Chat Completions API + +The `/v1/chat/completions` endpoint is designed for multi-turn conversations. + +```bash +curl -X POST http://localhost:8000/v1/chat/completions \ +-H "Content-Type: application/json" \ +-d + "{ + "model": "", + "messages": [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the largest planet in our solar system?"} + ], + "max_tokens": 50, + "temperature": 0.7 +}" +``` + +Server logs will display the following information: + +Server Request Logs + +### Using the OpenAI Python Client + +You can also use the official `openai` Python library to interact with the server. + +**Installation:** +```bash +pip install openai +``` + +**Example Python Script:** +```python +from openai import OpenAI + +# Point the client to the local server +client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed") + +completion = client.chat.completions.create( + model="", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the largest planet in our solar system?"} + ] +) + +print(completion.choices[0].message.content) +``` + +## Benchmarking with Evaluation Frameworks + +You can evaluate models served by this API using standard frameworks like [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) and [evalchemy](https://github.com/mlfoundations/evalchemy). + +### Setup + +It is highly recommended to set up a new, separate Python virtual environment for the evaluation framework. This prevents any dependency conflicts with the MaxText environment. + +```bash +# In a new terminal +python3 -m venv eval_env +source eval_env/bin/activate +``` + +Install the evaluation frameworks by following their official guides: +- [lm-evaluation-harness](https://github.com/EleutherAI/lm-evaluation-harness) +- [evalchemy](https://github.com/mlfoundations/evalchemy) + + +### Log-Likelihood / Multiple Choice Tasks (e.g., MMLU) + +Tasks that compare the log-probabilities of different choices (`output_type: multiple_choice` or `loglikelihood`) use the `/v1/completions` endpoint. + +To maximize throughput, set the `batch_size` in your evaluation command to match the total batch size of your running server (`per_device_batch_size` * `number of devices`). + +**Example: Running MMLU** +```bash +python -m eval.eval \ + --model local-completions \ + --model_args "pretrained=,base_url=http://localhost:8000/v1/completions,tokenizer_backend=huggingface,tokenizer=,model=,max_length=" \ + --tasks mmlu \ + --batch_size \ + --output_path logs +``` + +An example benchmark outpus will be like: + +MMLU Example + +### Generative Tasks (e.g., AIME) + +Tasks that require generating text until a stop sequence is met (`output_type: generate_until`) use the `/v1/chat/completions` endpoint. + +The chat API does not support batched requests directly. Instead, the evaluation harness sends concurrent requests to simulate batching. To enable this, set `num_concurrent` to match your server's total batch size and set the evaluation `batch_size` to 1. You must also include the `--apply_chat_template` flag. All sampling parameters (like temperature, top_p, etc.) should be passed via the `--gen_kwargs` argument. For Example, if you are using v5p-8(`4 chips`) with `per_device_batch_size = 4`, the `num_concurrent = 16` + +**Example: Running AIME25** +```bash +python -m eval.eval \ + --model local-chat-completions \ + --model_args "num_concurrent=16,pretrained=,base_url=http://localhost:8000/v1/chat/completions,tokenizer_backend=huggingface,tokenizer=,model=,max_length=" \ + --tasks AIME25 \ + --batch_size 1 \ + --output_path logs \ + --apply_chat_template \ + --gen_kwargs "temperature=0.6,top_p=0.95,top_k=20,max_tokens=,max_gen_toks=" +``` +The valid arguments for `--gen_kwargs` are `temperature`, `top_p`, `top_k`, `stop`, `seed`, `max_tokens` and `max_gen_toks`. The `max_gen_toks` argument is used by some tasks in evaluation harness to control the maximum number of tokens to generate. We suggest pass `max_tokens` and `max_gen_toks` with the same value at the same time. + +The evaluation results will be saved to the directory specified by the `--output_path` argument (in the examples above, a directory named `logs`). \ No newline at end of file diff --git a/benchmarks/api_server/images/mmlu_example.png b/benchmarks/api_server/images/mmlu_example.png new file mode 100644 index 0000000000000000000000000000000000000000..593e7694acae5dde1cf001dbb8edca98c4d60dfc GIT binary patch literal 63636 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 literal 0 HcmV?d00001 diff --git a/benchmarks/api_server/images/server-request-logs.png b/benchmarks/api_server/images/server-request-logs.png new file mode 100644 index 0000000000000000000000000000000000000000..01ba7dc064cc0e7a6c735106811b32a377835335 GIT binary patch literal 71899 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

I!t4&MaANn5UJVOe!00wVB*30kq%_IcD)T^ zOW)J?%0kJ|&`|2P)}m#1x75q50Q$gopXX&{1^9IGQBgi@W`|y(>3279g~cY9jg)97 zVb%+%XsdbvW_?J=v@I<3x(5hLV$ZO@MMffCacv5n4=8x|{GG#wMqZ3$;jh3;eCnwfv#gmSZ)NBL{~BPD#xdNnbTq24jDO!koCfoyl)*AdAOh{ z)tp3`euU&8*vZ~rU!ciZ7`Ka8#blA)1b!tjbreF8Iv&Pps`7Kt)U?2kK|v&g&9DUm zxdWgLSQfW?PB>?p3=-4PcbY!?GxTW`naJZT(p*VTG;V_?>Cf}MOH0w`lOF<};jy#N z4$Jj-2G(p+cV89baTzn*Tv!_%BW3Wq4grfW``I;Y&)XWPsl629dml+ELJk3G>tYrD&rafu+4`Obt7mTq z#Q!V7m#!jZJ1)QfuFS*WRjGGT`e|8(4~?PynKIa`N7^T1HK_L~T3C@XeWvaA;$7Vi z6Ys{Iv`LNpmKcYiSg#-kGvh4|PSDxbmlUPMj%FF#-|Aj*%Hthh?ckhr+8s(BS9Q?# zh97B>UTT?gXe5aPQ?_s5;Ln#q-(7C3W?UpBBrYgD(Cb;#g3bQK=1X>B-&dP|5bv_! z{V_8?-`(AfcE;tobCRIf`dVgZc=~`Jxg&$q^yxQ<((>S}V*snZz2n|vft4l%WndM8 z($1m5#`%~*VMJIWNTR&F{MZ7ejWh7JNtx~E8^mO>YKd;!QQ)NS)v5c3$6HNW7l@J0 z&enzoUS2m${Z055RkO=ZwcUyrKK>e=LYAhMhOw#X-Q~2N8-1!L@-}eV5BB#nh27`M zb(>+Wo&|;Xbf_?hw}6}M8f_#vT-e|zidinJ~Vs z@^cI2eu}jj?MBR$2fIbccrhz2=w%%|L%>zQWgo|Ww+kFP5f&zmgmM}Zif63ZWX%V5 z2CAPa2vcfQR9#89EWlHTF!;n2(~^_rurYr356j5D^i-?R>%On!WTIM?Q&EWrRGW$l z9&s8`QPGqn%_e8cCc$@O*Vl(4KAyK%L$i)sjw0Uvi5Mo10CgcQGN%?}ZERB%;H~jF z-DXd4ID&cG9MR5RkBXmt2(9ROPxLxfKeLP&E{Vwr|TWcyN$1ZHGckmRcoh$ zje2w9e0p?Gb~u(RkS_nLa59Rl4Uav{+-P!MAxp>;V%XV(jJ^q4s&oE5x+3n8B>?oE z^sLfzJpgUJx$#Yp-o?dy@ydD5N{O?`C_Xs&8ELMyH3;?GL+mXl^ty0CVNgUNCF^=}6G$zN zQc5Fp$*=#9hp;pCZo%-xz1>)!7UM5%Q#}1vg(1rxV}z5q!^l5S914Ch!Xu@MU|Fe# z1kzJ3Ym^sC7ba@@5Qpk+o}gbY6_zhRj z5OA>ol_Ou)u2B}D5-vV`{W#`WQI9%_Svxi}6H8#d)=Kos0~=f9RSQEXj}G(q6(aHN zFex0ogVF5UXptxZ$1^6lVyr@@APpU(fY~q;z@iv7_GcXj5>e2Gn54a1-Qk-noss~Z zuc@yuR5A!474pDX&8C0SIYK6QT3;Jk8+}gXdtasU{H90#z{gI$j?xLx$PjP@W%?oA z39DYyJFBU5!m9hTRE(_m0Qbzxnwy>Lpje5xOmJJw<$;?tHaKZFIw0Gc@=E3%xsves zJh7D=&cP(-xH%F-`Qh)AdQ2@C6bId4^f#5mezS_M*IK9C8)ejcb%xkiR#bf4X7Q`k zU|y+$V^%n@y$S!Ys?T=6L7#xxX>A9cLUd+TZ>7!k$mcD2DbtHI8&hd%X*z{FPfS+Q zfkdWDlbO+MK!D995OVK_ZVV(cybYahXZfp;RRUDMgU?!s?#i;LR|lB-wjU#igr`Q5 z?0h|10eDxuJn76Oub;4iYo$+6W-^etH(imR1vqw|K9etjl>$AW%hf5dn*d_+RSq7b zzZ&e`1A)`YAzQ^Fo)kjs;oC;+j4r>noTRi%PpZ8w{Ekr|fItG?~)GYfAb?;jr@|6N23*T633 zI}VRaNtqogdW-9xr=$-AG9`88aL>!)e$Ph;1VX#kT2V)bsz$|*zvRb{*N23$yUem$ z2GnV(DaCIYdj;K}E6%C28m-8E`Sq(kPb)orE-$}z_&1IZ4Ho=Bd83Jh=*Ga>c%dk! z2R@?#$z1N9Y-w!G_VWw9tg)IVpqj%wlkN5rP}d|6egmM@y>PUPPo);hx7zi0ln60z zMg^0e2y*1Y7CVMYFZb_dWsy}exHar(M+!;CizSNWV+SZv40BpqJ3GYDMqi{_6?1#d zs{YdT^0+915j16vWtyC3avA*YfIlWXJ5B0S?BSLGzG@viJ$de%`~&MgOhg=C1FM{$no?1`x@AFOv^C1t0kIwW;PMuOO->;#`Ql{m~{auTqGQ?k!{I&R#;` z7l?PU&N*;}>J@O@4UOKUwUHb#_{vgV$_0mQ^9uhsijkX8yg&d~_$HDaRi z*V=T(qf+AbM4!{8+eowGtHjv}PzQcj`Bf2d+P z#9{SJDttF=s!U5wMdjwQ@YK_%h|EoXoXuSnAKpr1IYP(_f_Wln19T&e~T)Y?^1^c-*0*D~7sVeC6lDn2ogC9SB z#Gwq50Ce%SPxZC6!A|3^RT?q(Rv?4opXxCIff8lR6$z=Z_W$5#gjp*I(z1m(Yk|g# zD-e5Ar5X@9b;$rO**mEnU42$O68J#%X~qY$#>|M>R0q^RguKzjg6 z7$+TvEsT^Poa@gPeJw%mo58whz&k5jU*5t+6DqB%k^<~ON~VrJFlJ1{ptwIjz})N~&B5($@OWo3aj#2RExi=xzih+QZ+H}sNiHh7%MgX9}5$vsTZ72p87Re;nj ziGo5UR7n#=u3*^&`WmB`yF)}HrJx{!=ktQR#vGO|6SK5t#Ki4tdu)wTETl6ahB^f3&3ZgbmeY7_UGD0v?(dMG6C&p~j}?)+D_pCB z0!wopZBQ2*K=W8`qiw!zzkXRD&zda$s|5hxwOHehe#_v+x6sJR#LhnUP>7y5wd&#s)E@yR}eELxjA&X94rfAHQ zCj}j^04=V@-pN>);-cKf#`GCnT1x75t<>pR%EiRf2ZL1cNPoZ0d^wUWIjyszNhT1v zrpq-km3J)wQ{xqaWX9Tst6wfAPo91xOGrUl8u15l2d`)q<$SNgZr;sN#LLCSRXq6T z587zU&OpKIn<|HmZ@+dpC8N)FM3sytr1}%}-ne1^>5d+dHQ0o@t=Z~QTGE9djt%CxvONHeC@>5he&QBYOunO&t}05 zT&)$la`6oy+=1qY=|x!YXW?7-H;!r0H@NeKu19*n^#w5myS)nx?CXWU+n?m1T4pPu0ZCgHV6j1 zLSmz?|Amx#=tVOO0q+5 z6l;AWpMXH;^zwl~hg+{CU@vtiB_mxfSp$5S`~t4|O7fX+VzyX;UeOI0lrYfPg0)_s zmU$(l@c3r8wl3;rIvYn_eMwIkLQARv?Y(o_{tldXUBD36oloM$#l_7X{D>m%@^gKK zPydjR!-XxZnNT5}m(OU^TFFSjt#kXEkvwVq>{-F6ux!>S z0lVYTcBE^DW{IYPy?B5TwE&)f)PjqvE7V~6YmYO(yd$(&ZVp9~$-QkR8LxH-X zbUyE{--=GdrbDR+z-AL=s0;DhxcGA{Pu5nN+Hjz$#V{W%cV@D=S6)%EHCrz-S@Qbn z58ICtWc&@jyYbSKsLE<8cI6s@w2vR_pc*UOxH~vFJU497Yk6Z!(qb?Ylp(~!bAFog zQ{`Jt6x!-l-7erTfFu~XG4N>5*MXKGLK1Wx&k9-cp_60T9q~9wlVfAj@~fq8rly+% zNfj)*O**qQ^VTkW|CR+uU%$sdRv6<9B9w}@ud#nmdoEZ;jh>ut=)tOOmwaI2p>}}Y z_F!>P8R&~E1jHNdRz{s=Po|YK1(b<=Spq^r!iwj5n9=CyyldlXQ_+R5F92>W_Gfrt zU?7d3>(}6dW$FDz-ugPAxr!!#g$7w4o}Yz@d@5o>ifb~6?TztdpUV{T{j)hs*N*He zUd^BXyU$VdvNwvt)#cFI%xd7=@(9*y(6kr5*b=riB1ua_!^c-UHa%U44;*3f-nVG3 zi)ad+MPX+;6J1)auo#8c`^XBE0byb6xcDkG*58+0g8K#r6tdVE`L7;>S^DOmRWNIF zi^jP5{*IYq&*q-=$&jf6eZR8D5b@$|sjRfStfpD(z2@Ouk5GFW64e=UU%+4SJ??&- z?pMrluGvWDe5)t+Q)7JmIDRMdHm9A{p)}E;Va{OB1|nX&NB^$tvroUJme|=97zRKV z7JLp}c0g_lE$Hj-WBASZKF2@j30PWmz{s*cEE0=cu*i%R8|H+BganL4OqFSeX@sYK zPfL>(BHG)U&yj$a&DSXt269_MQ445jXfU>@M=-yAI1YlioB$Eus5h$3ZDrxY{_dgY z18SuI7018$dzlOAc~GA`#)VcFYG?wf6zMsH%&3ceQ4;$rYZKmJIrt(-r#K+SZNDX%wlWmhtCa_Q_wQ@fKTKAJT+cST(Q z0fCH-?@pEN6)I#E#Y=<}z#-@z)gf5qL~K9qfk~?ed=GKIJ35Y$&kzW*(Kr&B!tO}r zn$u_gryrQ4q*Pb-xl5;ex3|Feb0kaPU~4OPRs*i;b<>H0oh9fkllS3n51CdJjXAn0 zm|srQnn2$}#&UNpnTY>N*{Zslclu~%eij&iLWY=5RJHU(a!GHbseA?|s^nacF5`G` zP2B)E^Z?^=BdvM^(L_d41loT5FqnhVW9J-o@Ys1I6LyrcB(EzfZ(+=S&~1?}$tE%C zMpm^$%+>nbUI7N&6WCYj#kbEZPB!jO!|+ec$MeuKL{UBj7TX@Apl$ByJwR}jIlN1c z12#uM^ah62ioA=hl34P5W?JZ>lan;U!iE5GhB*PXrKR%0G#G#teDqV(LE%YPZfAfu zckJxIu-Wy9Sipt!X5ZM{{IjB>;_ML%4&Guv8E+AQuG`hKguUw=w~laD1NE8a>TMYN)jmzUZB)RD_ zba121b(iR(X@k%0D`w`hAi<;~E+o@0ql_wDlk! z$jU;xy6s8803EaD$pD}Uo!?xl-!_I4{nKaW$tM~@{s&<$G+U}lqk>z~C(x1ry16VH z6qN1*iC2wx$3HkY2u)T};;nz~atIBBVxQ;cqM1qTQx3i$cK4I1S%%SG02j!vRL81zPhhaXRgrdd8ka z=3Uv5h*wz0-9dCeeoaK85)}AiUI}EfzCFXGZY;9dSUBrIW=x3@2)NWF`w_s)ZVMzxr zEq!5_k%7nA0w1osb!nO5)zuJ5dy>cLHr7N?xKBILKhvs3hDN|R5oJ1&U;0AZB_<}O zsVYa2EbaLr-`23`7Z#{18_*)V<9mCxge5;Pxv`wF+*Yl6o9=Tw=M@Dmr=vrD(C!N* z=HsEnU9V#m@uwL?c+~_2W{{|xfq~9N5FoI7czF23-4P6Ty?@r2n3%S^fcmb+a`Gh0 z*9Lvq446{wjSjJ$?=kbL!$2vjA*8Np5xUEO^dcFi|-E%PPdv25PZfx zRaRF|qVO@lTJKYCk0~gLzo^|C|MiR5n%i@&2PgR@m;i15S=U=&+!VEq^z?v-jge8C zEuf({@aU&}{tUO&EpITof5OQbIsEKE0M}?D1<-k)%F(3z?c^Zq=VLt)MX8niw_gu* zoN&E9J78jFdc|Y0zrFpwVVe8dzbNT||9n-rgBThFU$g*BQZ6no3R!}n>2h5_%9@zq z3>-eaY5ZNT+r*^9yVTWn(Klhn1_#WdBKLFXtiTo*2E+?s1_Llh@p~S&fWy$ZJS-H) z+JxfS=@qoJ<^XW^{|JpjFMKfP%ZrNMQd9p7nwLy&mSv51$``hzy9bH|n zchI#}z2FDR1_x$~fwl$;g-(_0vL27ujgEegql>yb@(lrE4hqKhQk4;k6!zj<lb18Cix}+()U06sOzUKgf~T>1+Z${o{0Clnav#8gMe`(2!!|F%*u9tik^qZb*Fk` zaEr;tvpmS7Fmxs$a2nb_1h8Sou+dcl=-BCCx?jzg1?DE*aT$$YWCVoV)zwv< z{UKjF5=oSCKVGIpa1jk=3fWE;zbr3jIaXIw17y!zfQBs3XZKGFv;)>HJKOg@7sq1} zJAN7bE>5nFxp;B0vGSP_uP0-(vMTcP6X0PMzW!eF|A9PBY`~Ie*9l6QZ??1uUVIzi z(ZSdNiqjop;(T51^FY8%#GgimH#Wy9!m&P{^%e1IH0Q8)rnlw46y977%4niEK6Nl(|w1H;hl zsQPku(`YqSO8q4^4@5*n^ch>L#+>D%cxoEmpqro8E6bT-u})DTYUS7uNab3)_CR|L zXg9KnEV);Zye_&ozgQhdwH{>@{i_8y?UZxM$jaVdS=o;E8)OeqEnq+SSH?MhU=rOP zNM6k9pFg{uZrdmD{f9O;(@Vt~&o+lu+H8W44^&K3niN1l66{#p*vR^)MTbR;94kA1bd4*jSt%P1tYW-k}9 zoi4YV(=T{1SL+Smj5P4|U5y30IKYxJRs)8pq}FU7D5oLhaa&pA`lr1Iw-p)H%fI5x zoNRmmMq7G5eq>k3iXAc4ION=sHP;U!NeBbpxCCXxew|J`G zW|Bf)E*WM3D413%4|t6LrC6|thmJELVjA*25+<=+L#PDjcAKTZ7q>l-CB{Xchd*`z-VP63RSIyS~wI0_33NVf2Y*FWS2 zCk9y#0Un*F4Ltgq%1Pn)Fw1Y2gvxgR49hd9T9PO~mv!tAZX zI|g-iwPs=kA?9sM8YgxKKIHxp*qFj>YCnz3vyD$Gj4#%W;o%$O@R#`GCgB58Vv@aF zGfXaOYE#YUN5JeRpp7!VgU2O@yn6KtAdUbs>P!>181Kpy5|WbWjt4&mI>*8ZF`mHW zlr*5tiWvTBDAhSY}Xht5?G|D$IU$@BhznhZx4jM1cSsQ~2}pj!Z{ zBp3`%Nj$si2nwep5&h3Ps$hFHrYdn)Xw>}>FNn}B2P>7Ad@weCZT|MUF*LpkjIm)U z^IfkzSJAY5;QG$tld8PW75zFc!Qp!MxDw;G&4A^c6nC5WowG|&tY}KDk0GJ#37MVw z;@t2Q0eMkzF|U9tIiQLIa~b)vgy@^6cCG=x2n6l+#xjm*055)WbU;9-hKLBvNn6`W zYHKJrzpOjWur~^HTn_LDJo^apjatw=fq=mL^?5Gv^^p;&%sOoP<|#Bu$;qdih_ss) zFLX!rw{4{n)*n53N|GvGn@SdM{PGivrVtVK${R0%8R*8wtzu&Me76&(0NBMKtjqBKZ{w4?$LA&qpG zbocpTP3(DN?|t60&ipsCX4V?vdG7nVzVZ29S5g+&(WFzhI?s;knIYP6vMYM*yo zxY?0-!}hu6ycJ22i1>IptyR=>mxw;F8Qny_0ichA+6L*00Y1JVT_XIQe5g#gSf{0> zJ*7kD^tnWgUEy>|*I+j17T9c{I1!S%h59|*-+Y>%pMNJLb)?*$EPi3mX!L{Im1dV8 zri5sQ9b7ssbFLnZj-DSZu_$>opC1`{P4uir$zxwZEW&6`cIwW^Al#mr{3MTYt}M>2 z_k3^fxSkN*eB#g&&Pd*Qsrt@p%BL!a;8(94W|wPT5_2oj!I4?wGTFjVsZx$MKNS@- z<}b4>O^%gJ*cXRL$stbVt;Ic)x5<&wvKh(s;?=@pcUg}o`v_=!*N;M7+VS*fc!ldi zA;=~sAxM4+3;B|)tPD^tK(^cPrh`x{4b3&Oo7hzjYj43tBH?lHbGsw%gTp3p#fldM zXH-MDO!eT4$Q~LC1DK_$iH_Z%jdtRx-rT}MJnt2u28EALkB^|MGFCHIA-iH%Rv$ zrqt%7+R7+s?=L524AZEw?B5**Vlvb#T6Lnv1eLVMV?jgcMIUNLBWsu zy1z|v+^sr?(IV%a_%~rS6?E>)dq|X0m=j6yK46UKe1*g3U@-~$tjGua9F$WZC1CAO zA4T-4On8w^lO80}YIe*=D#CW`)_?dSLi%_Av_-`0k-t1X^)*yHCG`gFNYXx`m*|E~ zCob3=dw5p~jBIb~IMI5~l({q_#*&*WKgvd* zoNH?oKMvlRXQ{y5xFIcl{dtT=5X8~mYgdLhWBw>fD=0%t8SYoevz9BzuBhk!=5p*x zGoKG4`_wUz5sRU4Px1J0Zv%`H-5X(fYI2XS^79`s9P5w2lL)H+ItITT7ctD0o=~>@ z{2QL(B5Dhe&a#;}eUF>`t>O&H`7E7kF7_K2JxaauAmP}WAc%kO%*1<4C%_`Gb94J* zcM=kq11jQ~Le4)7215iYqOD1nbV*+=9*m4zyu6|kf;{#DZ1BpcN!)_M6kMbT*&BD- zhRYEftMj8dE3X@>Z_nf%Pg0~wL}J;WV7G$F>5Q1Glc-JP|9CaB!ux{HdZ4_DekIkf zM;WJR1q)4Zh>A=Ty|J#EL859U3(ehE*Z7dUyBxIrQG%wsYQDrn-dX z#nt$7!?0lBn%uFWT1-6uu(h>yI(ZvBFk`q&$hgd|>myRfXWzr8OBQY+UNr&{<6HBoRW@LqmVmP#E6ZHqev=pl^|G&oJY(eK`vv3Iev@Z zW%o1K?5YepGs`oy_aa|60v0Ii(zTNWt zcdNGkf>K$TWixy`yTWd$dK)~*zZ4ZAp`+7YVBx+Xu;b^~#r^K>Ws)I5+h*z+T`Ah> zd8pP-1eKT{O)hQd@1}z?J5p-RZT6`+iaj-m;O28(()(g4vnHau6lc!DEif6!J)XhA zz7%z0S?4XIR@rV>>JZ9BJXVB-uKDGQ9wMzkgqW|VUnKdJ)5xI6pU{u^voWfK8=WjH za??VRxT>1X)1A377>g)niuT^;wTM~JRlvDwMIb0IdB?|-)_1qX3hYtOd`ZrmdlG77yfaY*SIu2c7% z&BLx;s)G~{4-*rU_LYk0_{7A-Gdjm#({sR&TGKw+;-M8c(p`|+bd~FirKP1!qP;}Q zXSq3dcV|T+M+;y0+V-@z2i(ToMq+baCnhHD7n=#y2e-bAgSXn*;6FCjZ^x}~H-O>g zaN(8*_zuDGBb7J%=vMY0a$8Kmtr4<|3ZCYyP1CZ zn5H&2Q3RCP600tQ8H<5@eJb38oRi};@~}T2Q5iyrOuABB>`v*d&1QzJDZLR?$Eb$z z)^UvIYd*AaPDUOTrD@J>xg&D^85$^U>7PK7`uu8~d1AtWUBTWZ)$mcr!weU=^h)C! zCR!g%2Jw$P6b~X>5p-_jt5JnV1E15TnwFL;ypFN344JgDH{Z52PtCZ^i`ir)SOnm; zmY5I5M>m&NWZtEcBYOuQ^(3YW*Mo3ol!;sG>pjK@Mcv;%2MGy@`b~8dM-)kA1!Z#1 z#^&ZHJmAo7#sv6Lu;0j%)V^+GpK@s$cQ=R3R3W^#JUIut#fM~Z{FyOx~QwiE>bQneM^yz zIotB~A>K=3Q*60Ij-YN7-VaJ-h&c9x!_qD{H-YGvoH`Ak1YK5Fo4Qhye2$2UwS0D%MS3V~_=Swj&O~=46(Sp=M6MI+R z#&1)BTU|tYMjD)ouD8(~#vEKbSSG-tKofXy8yY$(W(@m?e6R(;xKw3k8%3c^4J)tJdlT6 zFeY20++)C(WYY`855JEULd!MH;J(#_|ay#3^T7R5toAR z@cJQuVS*qqNp$n=dnMwac-P44I7i25RP@fXR^01bK1Rk(EtAN{2!za@(<`8(P!3Ui zCY+&&)uy{a{$_rx!wJk!=>Gx}!e%=w3NCm4JV{0Fmx#I0-8Oiv4zKI%!=r61$bx+L zr*C1f%-vg^Pbj6f@>3u1Qs7jEEDL#r()E})9g{?=I_wp$$9otFy3L)Y^$~5ocdsR9 znOtt!X#JW?p?v3#@j$109S-IPFXP?dcW^77uLcGfJ}&Nu4>mZwA<%S(z|+M<EVB| zL43{iAB&kFywE0{w>Ljnj!(3Wy5YNU(s)}h+RVzu`6>U+&bY8GNDiP5W(?a*PG;1G zC$q1F>#6hk=vZu@JeiR8-PZik*3u;rr~V4Pg0!7ZbB^m$AuFE3B~U1P=UxnMC&t=~VoS2il_GIx0R zl?^aeXu^vf)=0dkUR>ZR@4jEHIXxPG@LYoc+wrD%J$lXIaCy0046iqbf-g9apd5}J z2)c^c2Q5RRkiP9_$0-~F{Pn3`ZL=Src|v>&%vQ=UjVeA#IXNaC2O6yNqVj2hZbWP0e z#-l{S@6N~qI3vi!NVcrx){Tva5BqY}Vx3FW^$+bW1NwasEnUz+GUEHI6Ol-j8$kP( zulrRme84$acAv?`-ZCtP*QY3PdIxC;UtxU`vzFSqb8Qa_28g+RZs(yrgm*W5ACyw% zd|t06J3GE4cHCqS~O6O&Opf757p9MboGems}XK*z8g3OO~b zB1~FUotgNebi>}4yUvWa5wf49P^8lnGHi<{cG-jDG6w30EDbVY0u8dWH7{*5Jyn76 zL`To?#MZW_LjmkD!tF)58U$eBJ_V96Ff!T}7J*fTc5Rm}AMkVo>H`u%B!&VZ20lKk z+2Wdjy?Zq=H%CCg!iewER_Vz8+@5^>KQ09MZ1~S;Un;Szak+J?JXJov7zjA_EAf!m z_LzQlogMM1vG<%}*840FeLk8AOuN0Esmn!Za9Mb?9)K_2P|YxZ{qo3k3*G5gtsTgQfNcoy4l26_qfP*(EwilaLFz*?ZaUe;==;M+^y$*K$3CES?(vbTf;W=W1QJy(slf$ z!%zt5KoZ50p|2}RsfA%H{XAQQ&gWS?m2HEDDMaUtkEo2)ohTN@BJKx-?u6k&%d&xr z2-C!!onv4)346a)6O)FQ;kFHlS!kPnxCKKtP7xZFcEm9ZX6COm@Cr;X9)u# zff($EO!4J1$=8$J$q(#??ZOWNSghBl=x(YHKr9*}d3Wi4`4t9ei>8g6xBYs*$XAL7 zz9Fq}+n9lD5xU{TMCW7RxmD0%?!MD=e=xL*rs}wV#OE+)VM=UDw!#WXru0zo_Y^4@ zX*WvUCT9R82chzVy?so%5}!R#4x>3u+XDJiDVrZYeyo4A)o5uVAQO&ak(k}RvCE6h zw{#lbiF|Eo(e1jsie=UWW%f>$@X%13^+(89pIXy2FypOlZ4=`m?fo%ay5oOSY5Ibn8&s09iZv1Qj_WE zPW%9Vix$-@9<4p;N}u2+n5|wA$U@b792OCAUbp7kH#15B2%Rhl!SN17r)S^E>c)ms zL@wX27Y`;GA>fJ?CHhUIu1GyuAMmDJSG#|oh>&yg<&DI$PQW}O5Z?@xL;d(hja=s! z7fVbA-hTKXy7|@rk3=%)bV3?0WIxH#+F1n2b2cx}Vzi2?XW}3JL~&xr`U^t-3&Mq% z;+j=_pL=^7`#)cwX;P|k&df|~UI(L|<8g-jVy_^jK`nJqVRz=mq-4!KN79A)XV`VJ-L3txtd*cIYA@xf;%J(hL|EDUvA}bMn`wx>eg&op2&7p5=ck>SL--`=znPt$G~recZFawUaQ_5c`g84;V$spjL6*^tU%F*O ztGB*Jh;$$a#v25)K75FPb|jzW*n>-q7k_=!_ETKZP6^1W*PB(YHy7A;`@gL)GpI7V zOZUw35n)WTLUxLBcIFujARvcL9AA1FW|{#K-_Zf?dTxLIO>auPCJ^esHToMg8(#T5 zg0+du#@`{$SQc}2j+jrWlx0!TE>m$1AhA*Af zSpE23!oG?n`4otJ{*+Gx#Wo)~h~gI)6ij}X><=1AZWTvG5_`sSfDS7~B&C``)0e^Xq>FP#w{8{%hlIqi=pI7uwmp_#!o;U!hafVLbkZ6u zo@s}!X1$^1w#Mm#9SQg?29w4*Zb`u4pIr~dc7iWDU=sleu<`D zRkrW~d7iG0e2hvrr*}6ywW-vPEoZm-rv6yMw@>ZKGrSeLQbKHE`kM`5!dL8BsMy%( z@2*`S+#B-x7a4yvl-H=^Hmb($r1fpV5@yYc`k2(-v-DU_3>RRFmfNVKvu}`0d;fX*K+=6Fv&|dI zxK}0x-Co{(a(z_3X~zsqAu0};f#UZ{Hk(MDm_i*L-Sx;GxNU81F1k{X{|8=(M4%VA z=SJZC0)6eR&gy#GkWvMmURJ};J&1Nz-9ULHQDLntiw_clhmg^d_#mGNX|q^&*{d6Sw7 zme_Y}uTJ8IZzpJ?_*brMO*MM&8MMTpDk>`JRAOK8$l-)L{)|D2l#~>3bWbzYD=N;n zZh=Ma(2@R7!tbM^{~8)v#~-^-?iIWK@Cx`Hb|Gm)Od1mzjf;m{yM67SV*Ot^xq7?d z&_&Hv%L268X}Z3r=Wopdoq7T9;qc2!&x;toYz6*0!?v2YVOn<5MH9s=3?nqkk+2Sr|j%sdI{qETEF?OL%2EV>7@W6;@8+8c{Cyl$-GnLKS@rg zexR@K)5o#%yeO3ZfMF1BHa6BfXmB#sQB^uBjwHQXRDjdVK?Av zIk!0Z*up}dMi}R76~9?a?_)1k5Q%#)v)UUP%5kHv7D8qO@{L_Hj5Z8Gx}-vov}uW8 zl$Dl#M=T!p0Bgp;pks!{|L$2FbBt6>l4)u8L!QPjUxbGA{j=?ViN9%QFqvi4DVQ@c z6SO-UlVnumcAiMiG;`j>pFpcpWeUJaVX(*&J_dsDzF|_zrPuzf*TUESrrl#QID>S5 zmj9P@PwqANOS!L!`>EW!yeBFNh1|eL4m!FhCti!?tA2ARP^|r}+>65TXTBzs2zPot z>;`;lzuv$9IQA+h`#_pvFDF*;5W~0-brw8AYGPNRTW>c&uM3w)BRX8 z?GxY?Z_mBY%0d9kOG?u4gZ+sBa`1S~$7e`IMMZEfP}V$5-Z|$N_+WfXq@5IK8fe;j9l` zR)#8D_%IRx70aZ4c<~YGX<@+0i!dVw#pIUFA^`Jlr5;us8n!0{IB)5>xg|?T!ZQxq zZvAZN5r0+fFFke8m7=_s8TneJvo;qYoa3Y`^_E=T;oODPrcg_ty=;Xk=5`?KL#A?j zd02vo67}5+;@NhC0(b>C#f%iBI-8p_U8(qBHEFOhvkiT%=|v?)F|kwRCZ^>2))p34 zie|O>%k1ps|cyi?IWdUYnk$yMl4 z2AK44zyN9UGM}uhtf!y@3jj(`m4q!HZ-l>(0pX=xeW1Z)F3JmBFq+ z+tIc_tq2bfmpgRK9~+*gREvzM+f*`N4z6<-iAn9!_1D{qdeuD438M%QE2QJa;yPlDYN-ct(oW#^}HuIgmP{TQ!>36(Po@tiR$zmp&LGh;|Dj`cXw*Z2orUs z%=Nslx;sJ^tjKBWD+0mLsW3M`Pa;U__7BQq1zfqh2m$Qd4Uv83)VJmzwk_=^V}fG& zt|k3c?Cpw+g(IeiO07%GV-=KjpQYvXi9J=L9=~jg^k>6Bw!NdH<0*6X)E0^~(5>JT z;0xVHpkkcgUI_~c(I~L1>_9|CMY*s_bwoHv%~rV{IO2d>^b^qN(vn}gHUYf@a_k3H zuA~Ak^@I4=yimsJIOK3}9#ZmGI$LLxjBI@}AAvhK73AKa-TiTbU?x#$!}H4CW5dXb zio;F9g8C)8Fd}{DaSwQ}J1V7~i;U8ExkZ)`8q0TjrtpV6$avj2=CO8_S<~*?F~BdV zP;0WCc2`GN3y{aZRX;&Q(ELT!U%1JdlQO0FA2JMI;ke-ZWEgsle`gqqeAN4gsy|us zPgP&!>Z9#gM~@unL^*EG<&~A~17C`YvQ4v0b6zYWtf}>=Ghj|la1@U*+G4mkt}HU| zm!vQHmHdVD4yyXyyKB%^CDE;htr%IO8Hgcx_@|`A9?ZeTx4R(XyhYiwXV0Ku2_#{! zs>PUGb63BxqU7?JL0G`pCgUL>D)aXX+S|S(D?9DH0?#!l1-^LkBF%vO3bcb*WM!tv zM$i2e?l;yZC1@SfaPb0?-@m_7{O}fsbHAodNd}_H+Wn8GW3;aPlW^;43?y-1t1Vrd zJdCo~00Wzl-2hC{g$S{W<3UnpiNsmHoj!a_hvH$Lp0500Zje*AwjCa3m(U|24jl@K3at+g z#&tdlc}zO`RjwB<2JEim;#!skCTE`s#mjhj#z@O$K0&}rcnZ`H-AG?WWo6Jqy-X6c z%5Bgt4&JF3-SQ}NZcnK2&z*x@xsbz`+M(Dzzj1|IK@a0M=a#=_sk2|XGEK~Fu3c@l z(b=$p>7L5_T)&E;=`Z%(YzATe-RCE2{aJVX!>Z7C&WX=F+K_54>h6zjep1>{G2h+6 z%jVcHRi5`~o((8FlZ`L=oxbsKB2aUSit=)EC8gx58m*7sgopP+^xE0!17sr540q=R zhlhvAC+=X#2@keZ=Iy{UZG8Nbc;MA4faWs~p^{rGUw!lgOc#LHHGl8kJ!qWNCcg5f zr=xp@iC%}8z9P(2ee|=NFur7vM}hWly@MGG!z$NP!T#<0Rzkwq1?1cJ*VlRS41g$C zMM_;%baOIyc4~?RA@U(&RQPrm_|j@8Ye4??{V?(k7Du?eu&rEnopfo*7Mk^}&SyV+ zytQ+3xl0~i4iC-;1<9v1=2K_=c^-+!DX?hHnR;?TzhFa}(dLob#Bd7Z$vC@=y3@(qq)?SsUB@bQVlFTEgD`6BTkK+-jbOPX_=LkqpXG@A;mkJ=kF#(-7dCuyupKws z{*b~lm%o6mwBr8K-*Osc3TBph- z2@RIpyOXuMK`O4Gun_kf&YOzZje}>Wodx133$lfKrrI+@6grBEihhxz;nz@7v^s|CKOy4`)TIiSUYGQL3YA_M?xrL6}0Ci>sv=6A&Bt7HZ+&PIC za6Omjb9{7gmyW;8-ggz5q~VR2*l=e@$J{`d$ehdNt2}9$nS*7vHxx^!TRb7`f|%X2ZUBOh3ipUV<>a-A%w90pB(Qdd*!@CRnTQBMX9f9@4#4EvIF zrE4O0GNM@xk(``^93j`lzl>9-52|`zv*)j?M$Zi$^L+!(dvq2_nu*jpiOqxqr3jgr zqf)h$(BxI7l6-smswO&P-P<4bqCyyT>G)kW79MDJw2BMdkWcZt)S|!0{d!M1?NC_q z+hkwv5^z}{<)l$;PIc+a1dVi%581K)7Z32$Fkj@Blt7Of=W4=K=k$$AC!x?zt+`j^ zn9H%ORG5@ulz28X-%4vDaz2!9JsoTN3Dbg#Qt$iA+SBs`scE^NdwNtvMEIGO_4{*# zosKf+_f1Bt;*!0^0DT1)eAHPs`SPd6O3*UZPEFRD)s8#|p`-^W5lreXQ>&wW7DIkj zC#QtCsNlOL*;>${sjO&Z(YyzGi<%}ClZh3Kwmdu2^`vHYD2DqT8+$<-AEuBGA6GHg zt508>O`IP5@n|pfz~7P7N9^m-CyN}N>YL8XF3YL%x#A?Z>@4k;W22%RXP33AVrSdM z?3Oz9$7}asgGfGJ!1Lv~f#XBd9JRsolt#Zrauh2LK&1QadwcoWW7vjGpH>WJ+qBdo z{FJWAiN(sv$Pn?oaRH6XO-AGX_%v06@^v(r(PYjH$MJQ>LlVl>pzY^!ZC`!jI z<-RG!n&WNI*52ON+1b1oDngV7Z*borFR(jH%?4g?ZrY%(YL(lGuxL2!ei>5{s)#5? zd&I+K+$l6c?Q^-%hpdZa&#h?-uW5l@cgcQzkbCr^wzjgFP| zr7KD7r~fD=q%}h+p=%x@HtxSW1W5sLKZNZ+=}Z@pZ=^fW5OBNI!k7c{hMI;(O~MC9 zRCexn=0Xl_Jw4t|Qp5Nac5$zax`E5CF{pOn5VW>KCY^j+j-El1z#`$d4*qql1__OO zp@h{oKXusIoX`r*A?e0pBYCD0?u`qc!;((JQp;g+5Iik^iMFI0=-iv^|BYt^sSH7 z5sO+jzrBsbeVN8?#OrRX;%G(h*Rd{kiGF-=28XS&$WK-Zt;PmfBG)^AX#qw?1_pMJ z_ZN+KQquvcgqVl`W_?`YF<^`d$4w(s;~bAcPV`fgp481e+*S*zeuzYK1Fa#`$|?uzIbY)VGqxg+gROG z8GnJXO}U&rCONIgc&eVW;{248I=C~KKpIcn_ve&Pa^560x=JyfAQEZ9xzBJPA5Km@P2CTfI!YeW zf$qn9sbKHfA8+4EKLav%wd01}*TUPKZypbqm8!GX)p+`{HZi<+l6akoVf*Zp;SUkw z6)LEc{{va~^LNn~&iq$i}nRs0_@p?N19GzmPwKq$U6`yAhO_CA&82izR@veWNpCvUGPeXi4MnZbRoMlCJeyeAvl?NZdLBbMfN}|t%?O#d{yl7_mM&T z46p_~_C9*%xw!$Zqhr3fxYy3kuEMdfu*hNb0}KrU5)#PoINyWtCMpVccH8hPxNzYD zkXp1qJg9<8(u1gEvHG%W-;?drQn3QAPAmOYDyvH}>yjj9A!s;_x9sN|iFuCQ9Grum z&Cjt40I(*9p&e!ioi;7`U8rLv#qV$I`FyNGPfYfz7j-Z*H(&lXq~0iP0OH{Bv7kVp zKgHIdy_!x@(D+)aK(|=j)r4Qok>~K%_)pXCpl*}@IroP}NEnvp;QhkIymnF8P`cRd zot`t&biaL4B3xWjEyAyI7>zZ*nZ``7IBH+Ih|j!cu(4|CYYZ?VyQQz|t%+~no=pKu z5|S=n+%j}uAysqx#d^Je$oRN~KTnE0#`POFVmKV>uRQDg@}^QiaKALLSFydlOP0QLNUmV23LL%P!sNsJv%6-?+O2RLg$_ETl~JV*FU!U@rPvICZlQ- z!PJ&PPw%pId2@3UP#vA@ofHZh>VU=|ffD8GkdV=;qjsh)Ai1eB>r^@(6hn3a3@ZPj z#g#V>^cnP!*ub@~B=8`Lh=^dOr(bA#$-MEN)`C*tR4DDlU;lB+M6h*jx z`ubH$LPEka#;<(2Q&HV9aw@U;aBN(JhoJT$j#5ai2asIIaDF^r697f9C08IiacsT^&>Q&$P1zQ8inD%$?dqtzvT9M(t&sHLvUnZFjbNt_!*Z`BQMl2 z`?Y+R+nHfIL4D^dGL|-^)=w+LxVw#m#aibV))JhLpU$DXXr+FS#}_>Q$qgD??nSyc zHA?tz2qGnBP%)Z^TQ703ZE;+?c8$kDcK6G}O=pR~tE4QCi7a$S{<2DE{$oT9(=TuQ zoDg|rPoju3q+M5c8(U-C+$IUK@o|6~L%{avRd{biAPRVx_RHw+bdpyDr)81h?vHk# z5D|0s?mT3Ik>wI@Iu)w=ZZD;aa>ckVecd9vCX-a4a+7RkQRUvfQ{@5XbqR=wJ}Th_ z*g~41NhxJ*COx0&pL23g^+N6o*TbJ*hV)Ac*gCKpi)vKf6met;LM=xeduJjOIQxO~-i+@6`| z-fg~6%fY|x&?UcZUT0z6fLpMuYsJ!1^|y@GyB~hm0)89?(vGqIyI#QcA6|fAQZR_X ztF+t8Jr`3{v$ef%w?hDDRZRK!dYVEoz;;)7Zn}&G31Yzjbd zy1N{$XK!;{s7l{n8HKS1`I^JmefS}^Ih@c%juv!h!bO@mi|GNoeUOy57)oM|8y`73 z=)My0_boMS*i!2>9)q>C^@|q@iweK;+!UUA>m0NU#5sQJ8ME&9?$Eu5uRWVfJw)_% zL!hTi!C*GqqM8Gj2VgAxoFPL#o5t5H)eNt30VpJNOA-c5cQ<~4gyn<|9=Je$*_sfZ zTxC4kUBj`SLBNHUQZyfr@jg05sW}vw%!eUpgBG%-H!?Z=~i_V+f^9>)6cQxyz+E9HA- z(icP4Ss2*8oFJ|HS7YQ-xrv=z;N0h*6`nLXKl~KxC;89m)M*&eTNpMo;BPOidl|m5 zjFv$&j9j@OcD#;=sfGbmbAS^kQF!F+Og8Qn419xa&-Ij@Z}ag0c5zLt{1@M16$R1P zCm|a71V$ktT^PZnGeaEULqR_l349~|fAWoD-~g7F$BOLfupzCjBhy>i3!7C{oxbKn zHqdrWB((ds8|DNrHFRB%?;b<0 zWxEcK)8^{t$EWTh51n_88VVkRQFZoNG3cT-X7iZX9H|Y`;RXf<019guDBWm_t0v>y zv=`DHPkBjvpTk1Vn=Vt6nbv{12J%~s3=FtVjt_s2kBw*i#3GSy9mC5Xj@b4K{dcqW zSTvbYRb?Yr*}vg>6~xIWQ>QX$6en5F%0W=-j@Io%{EP!D8FbgRv1gvXmr46ot?zD; zMRR%Y;_?}Fr`-h=`h6Bd;uM5y(^XANU*7%YhI|xyV>i(rGGG3}KXk!$?Bf{)^`9O} zgyBX?#~Gx394nS64A?={kBUP0-f_h`XInG1D?-pM28Ijf%H*)X>cx7%CDhrO#oV;s z_X~bUkX>E~%Xu4Yi;Ihn+e`ApvzG zg}hI>&LRDml`QQJYVhWpWV2pPGGn-0flXq@dQC}@#MIRDql^EnJV@AviW_$|-qq1b zST+bK8~<82{L(_;$$IRo<-EyH6uA0^rZ;TV+u;9mv2ia$Q;po@n=KYO-klG1#~*Fx zUjx3WA=5*(i#=&MYMMsZl~r@+en{)fcDd8?LOcYQYaB#%mXp)L6q=Rwx~`_FsR?~T zQ3T_@Kq|2U4h{}44dW7L(NRG@PuIbh*g*V((!LL0xQ+Vio$kz?mWIB0TK2`Enm%~T zs>!T|dF5TwZE9Fl@zBN#DYKl~cE~ks*Qs#a?s?RWw0&{+4$?8X2?nc_U#Z`mVnNDa ze_!&W8e#x0&@wX%x;ADlY)RfI8%h6C=#bNiGFZGquBXRFHZv$;6P1}4=iM8j5Z2@E z<+L-SSy%OUBGj{DiEC2&f?-mQ_4Mo(IgywaCZZMy|Jz>6&%G%4(n43e z+AmqY<#d#kn<{}=>D_8C|lbt_*j91H-4#=B{b$B8UG91E?~uVwCwRM>T?kh zzq9~bEQNRCu>#_-1m>sgU$k8G;9trQ!34JaRAfp@aznbSV?zASj;kre1O%U(n>)Ep zaoQg5hJ#Ga&BX-=>fo>tl~<}yMN!01_8Ch{6VlG>GqlO=(D`nJYPHR@-osg>q1YE! zC91EVo^H5xn^iMs3itTHJCQ~SNd(Kl_Dd)+@|}smD;_Z-@JYA3>hE}f?DG=K+&5m~buP)^`0Cl1xaF#Qnr|16d{bo6>_6j=phF#i0WaYD zL)e1si@pI2PIc_RWl6u-6crs^%fZU(&rA}y4Lg&77yzF&NiiuuK`NOK(ZbL#`0-Dx zD2V;%;qQoIrYE%9S?Y&{^T88Id9|8eNjb%iJ$^-AU}KSFLZ6}LR(Vt3V5PUV(ZI)# z_|)vyH+CWB{xmjL^W^wwmu+)y{DP98P6D(U(**s{GBPqQ%p}d&{f0EqLg32h{N7~H47SiQ1@Gf_K$fF&v$K3E!-kKYiBFJ%Giewp|g}Wc* z>Sbbhr-e3$q1^bTPER?g<7G-4Nv~(ajKvA2+Ou6;5|qeWhTD7AsYiz{9K@zT=5^m4 z8xKj9-y2cXJWS}f64E_`CB6W^A6{}M3fjf65JnSZ7FyfHls3YAs)Oo{@x?z9eV`%^ z4~f>kKG;U-fq4R1VhSqt9vfn+p)bzD++GTwo&oIT)dIHG3K&5YLu-b+ohVA85823k z22-2a*b^|`=OfY8mA$&!Q+odD_+F==>qHh^(bo0Ouktj^@HoTA!&{qvHiw4;vfXZ&Ufdv@RcM{Lub~lq9~XM7CMJ519&N&8U{!=k;!B}dpMP6JP!gO2 zZ)iS{EgAJ}4a|^0&l=LLld?cn**n~0pB!L`#t2!e6Xl$tDy$;t^;Vvmwwg$Sf3c!6 z0DC#PT=ejbVXMd+daSoi2B4M5HDFB8dzsq#si;CMc_bn~b0grnnIKw2%`SI7k zSw|HZm9fsMMc*4BxXX_}$KA5U4o=n23}+eM8_eeB+tIpz=PD6Sr8f9IIrt%E4mIvja;0shIe^Kz{PzmIJ|H8_X zk7#>E<~`53?@CB$cse({wVADZbihLKe$TI{TH59C&a?E85F8!etFr(X1e5CZ*M=l` zbjS>owzUim#l|E3ya;Hg8$iO^DGfoMA;s``tqPxx+E6)nAh~ycn*L-vTjf77o>+1B zQ)Wnq%K-!o(fUAUL*;J9OP5|kT<@>75A3|Gzd28@7hZh(yYIw&$5q8**fcbc^@QM} zvY}lT+wm=4_EP qbG%qC7Psh6jgR4#UUfag>6@p_*r2cIZ54vt7pYhEb5ZS5CJ z>Q_c~qx4*PS6xlEk_n?;S!VG+N*ldhT~vyl*;<3!OJDU34Fd=yeS^9O)h7C~Zz3TX z<_>%VDyQ3SB3&@O$*mxg#ZR|p3EAt-H)Ljh{WMkZu}R(KtTAT(QW)VXRPz(&o`E3a$MZO+P8dpbO~^HQ6bZ42eYfX7o(Xrgofi2-em`(*g z;^fEP3n5_?44?=582xSU1vUTI-piu|d%RqArL&X<=n8j)}MTM!yfp5Os?!&uY%t)>sOhZcW%?5ju`9S2~k? zEfKWa{Je&9ty!uCa$+c3ifU@tZQ@~@3drV)N=h2#cKqq#aE{hGY!R+4l~*4CbFL{} z_Rbx&VGNPs;yloC{*Jo})~>9(YZY6a09cT8Ab3IAhb&>N_uZ|l7Xf|b&l8F8ZQ9oL zI9+6qP_w0&*sV)qdc!|jYK_1BzgTFm^Z~pjnh|+50l-s^Q96~+PaYepiPlF!cs!H4OWf*m|}-kf^Md$1MzFw#H8FJ>pspumrc%8okCp= zF8)|H*?Eom4E+rKN&62;!V(x!h;&{=XBtp2aFh{!#S|hGc@< zj15gMx_hyUKc27+8nM}jUJ8&-IUYJaNDU;Jm;G+3O+_rKQMl!uZls`=>_YdX&+K>i zL@iIE=RuV=Hg?uU)KG;yY)UyTme`=eUbqYYfoX9+e=0&(#5Y^5>V>5UMM$cQR+U-0 zhsUeS?i=gt2QLGi^_B?~L&5aU?!i+R@4#NJIy| zu~vaWT;T!<`j3T`v5#9_US#8R$aoPss2(cc&+W8ZZK7ctx{5k_J>Gtk7!Q@)g(R0yLELJHcHW3Tz+%Ai1{L|E7?VRRFU7~IuYhi9nk%F zx=RYPr5J=K1?KjI(uYm~Dno24557-qCXI);7&C6RqF;jgxVgBP|K~39#g?Y@m&M`t zoSYf~`}yT2MR<3He!#>8_7{zGsoxIVH1V1z%!gY4sI1^LvFPMj1laUR+h}1fAHfb= zg=0GjPcqwg zB|SNW=!?-;<&)m`?vh6zlla1>zuS1C6q@C~Y&@*)azE$(H5a7sAMiw&k@)VjNisn2 zpA4dOKsBdRLh?g~fe&SiHAv0;_zBEukjtOiau)TwYq2q-fF+Qjrh32ktNQU85B=1A z%Lv-vS%fK`tQRj3F#73@g5uQ`lxPduGMv#r1tnx1{r)d9?tY(PP$oMKQnRg3_+iz7 zc$0+l?ydPzz5^PTZ{f_ZctV=^{|~&rG&e%-wrUCczES%lD;Q5#lmKe5f06jm3A!I2PKaIQs1_$80@5u! zo18Xg7=`%$)_&oDaW1&wopSN72xTJcg!KxbN(0@Zsks^66YPvjTizG(AqcfT9ksbp ze~h$$)KQoYi$Ymi@KyyIpO%*9*JOck8f}x?9`pZ~#42*E|2K$L=dGGN&u=rTvE%@L z7a%{FWKD?XCr?beKxSWG@>~j!#kF{_>N#fy908}p-8HBqCN1BVF&VGbNs+y{Nqoy3 za_mzvwU)Q~$gB<8op#5xAB=JI^S8FPqOeQqlX>aTk&2FbCj+pQo@={d08iqG`Ii<@ zFuS#BlPVKs^Wa2##0-Y)K+74d@86v-Fie#b3k?k~E8XfZ-;w{_Y3s>lR#l=;F~HiT zN_rC@RvJ$`OW9m8aWWJ7%srn>Fd};KdP5~XtHht?o)CdD4ZSu6RYJ^aS;xTZ_oHe3 zBe!ot@L&!;J@dq3=l>OKb-@ba=)b%@%v=I4Gs)EjPOqrA>SY#IRspMtM{pOT44m&m z0?0kBY`halo(>uB-nmZht6bjz?#bJ?=2+?XtIcIo+rDzx$tpLan-A2GPkrUh(y`?KU|#XCe){*m3|!2%mKM>vR#!6)-e^`jg_Xw;{D02bTFLgR`O0*H16#}>fHOk|fUE31>v=*L$-NX!W;flGTBYOC$R zb}rnQuo?->4bwx_um470-M!?Q*k#zJh;h2EXn<)?hI=uK=*K7r2yTe>_Stfss#5-I zegGVd=Eic{Tix0kZ;c8I8wMNm_ajCn;y++wq5juQEUaZQnIA~3dvhpOfI)}Qhi0MW zv!7NP`B?tLgL{y(@^h@o@{5ET{)6byP#OL>Zu`KL6k5@m{hgJ*AYzp%synWC``*Oy zsEhSQ!L`Ll43cI4>UvGinZYyHOYgQNy#Pf`b0_xm#osF!`aB6Os@YTcxDc-@vU=iW zWUxaz?2AF{G=ms`c3r=kMz!mKz9gYO%{0_S!8s!>R{W=J_H}G$dDQm}HXjiF-#`ik zfr7A2#LUP z^8ph3m(+Om1+jGtM#t^L!1l$hX1&Jq(Z_S@fxks@tYuf=kxlVpN7#E)x;xIa>KIvAl-REb zK!MQc;Si5IS4=B@%;G>7%uqyJ2p|4nRp4lSpj+p&zD1Y*b0K3H7Bc)$m9lk8#32~b zIo;w?=glsPa<2awoBu~Ip27)FaNuck>1~VPxC>Cya7C^*7=Ms~W%JAa@ z!YF36qTBKdQsG=)Aic45g$TwJP)xVQ)J7Q**3oV1TgJaM0dsEwEiK zfXCIx>5sb3_%Zl0*?K$>^!f(!fr@HgzqIeyb^z{viCqrRfTa$Gg~JE7Wuq z2b=n&z-%0M<#N1>(RwA#hk%y(OvBnE@+cB7_T~yO8JkPWf~z-!$U7ou$EcAYHSv0%4U=qj*f# ztN+3Zr`{`HdX_C%F)@Pjjt8q$B8VYR)BXL?f-_DjTK&s^r)a%tbvfJ(mcp%EE>b7W zYnExaPKSx-l?r4iNcJk{ejp8xvb-hdeQsm0jRly^kX$(BR(8@Z3w$05XCzMScSk!+ zvz$SwLs3ESume{w6w{d>Lg(af+G8kUh}q*{XX*R(>HAgdH6?3ZsOANTFU5_09j6Az2G*!um`Nr|3sJVh3JyB~7J@IU%&S%pev`^nx({2V`C zGt3l02osODMDVzOlHXtxdO-!V2ppFydY9n6aZgp1jOC3x2p`fBH!A1y*-A@woUmZ4 z?AP!ID@as4t}meOi8)Wl%p4JRg)JqdC@c&f99#X}p*RP`d=3ij+N2eZtnF)Rc7{u>;7!p0-N~@36|y4YL)fF+6_!9^4;Ye3%;XS{6_&Gch6? zDsYZ~*HXQTue(4Tr%846>m? z_pt{?bF{x-I(J^|NsjWZ(CIc+w=lAgU*=P+ImwW@8}+r-p-ONMkz?-{k%zqUjaJ8E z2hS-{9|@@j|6j}=sSkbF%iq~sd}k+YJZ`3!YE|03hf^V#>SoW#S6U*NebGs(pseEO zdk%$>YNDmNnR4h$Na}a+)+E+1@Rnpd;Ngh4k4v1(tGAco$4qdzb}qM@vm5OicH5{N zXlSP}B5@$`1u8%sDbq}Uwk^u_c*s~#kgT;aI%i^%43?y(=9LtY(qh~b@piwvF2*3a z&f$Jp)Q&k-S!-)4tN2h*V$Dq`)}w@l-0q$r6jexAA#vT{xo z1DJ3xh`G1t@+P}G$UXv>1O8v>Zti{UF}0C{^$(cy+1|7aIxR&d440n(E`DQ;Py~bE zw9J!ESbOO&U7mmlY^z5GPlTz#-+RW!pjj9xSCXHvm#2n?h9>GH;3i`MtVFmW!0Ss% zJsx8yMjpmS#+Q8Zi85Fd)-O;*>^prZ2VV=T*t7a1$f8QS&Pdl>pxKlAQSQdlo6z2-ZTh{~y}EI;zX8UE2g{5K#doMFgZvx zq!FYM>5^_x>29S{xXz7^||~*SuAFJe*4*b-&a{4^(6+VsCk=;NmA%=fsvxI>`T&2{S1rA78vPTGc6Z!L?wQ*C z6ZQ0Grf%6s3udx_R*I1JZ?uJ-f$H9`ql%6_JLt3*m?uSbm(qhJWtlx^4s?tMF+V0J z7Z@#6Cm>!^s$n{A>H?ef_Sn0~d=zA4C8aW?7^EvZu{{3nbQi65B42FXdf3*~^yVdh zBl=yhN2|iZwY{UG1U0t5wGN`JW@ZC>Ng`>r!cMO7NNb%4%QpR@HT&yD!E;ckz^Lzf zDu?s%j>jCgA(bIjLS$A`<9SGz#cI=9@0~gl z4Gz)Vwm&(W%U)w&|3cY3eNBiuf_bU@`kS zd$KxOsjIIqFF$tU*Zl(%yAM_Bx)??%uAq!}3bjg`5HVYlYdcM0KdszB@m$s9O%Fj8 zF=BcFI_JIBte#XcVzl0b>a)G=1;%|7@v+X4B>O$^w&c%!+n9OhQK8x&COR4v+qs5X}Z?eHId z>5to&Ze2^Q{)Gk9-wAtqW^!{`~BqBuPZ%d9=AZ~X#~i%i4yPk%=EWYe-b=Hq<|VU^5!5tRL6Mu|a6 zC$;@l>7i7Mq>Y?D`D-6ja}kBx5RXQJ{(s5nQauHyHjLp{8c|Cv4PC*&uCV^InTX>mqv%W3>f<~nCd(60;vS!z z5HxZ=J=>R1R*qr49~2gb;I3MxwE^B{)#_KIho|Q*Fra6kj!!~XVs{g-){O7j?By<8 zQ54@_EX<72-&q*uIK9zR#rgU6htv8n@dHf~85>J%necCZp-5dFozsH>6`PFn z`pYb=TZNB=g&V)T(JvjhSZ$AzfNsa%6SanOH9h~;F7%V@NZ;huy3?YmX(&)~8TUB` z>KkpxmXo>)ATW=?FlNe8OG5HZP)G=DW8fJV=%u}v%)a-+cko*`HDmU_aWU%8goTIu z>QlUBA+4alH2Ma{x=# z?4RzWeFr2d{x7*o;=y9n`MqfbO-fG0af;GIQ8>YjqEYzQ=_$yxYYKWt-)J9eH(mW# zcarq*$!gz+|IVKj^^g9fR(s>!uTyQw3RZoHpw@ zGGF-sP8ay>Ceqg0THVqkbzUO)7ea*u0t#X{af$o$KmaZ1%Wk#!WUe^B(4D6S_cHrN znXoA()}^QT{H*T@=ho(y1aNl#xn|{T$Gi*n5y=Tg@NNQvFy4;(RfJLnslhLf*VsiO zsghWlU88Ot)Ie_bNW=BM;j&k=!zN!2-uSu+CMbF5%1GtUpg`Q}GaNXFa&JHKLr&rA zH5KY?fpC5AeL*ALg|NsDNb`a$I~Nv4=hGj|hW%JNkVRVx1to{@Q6m{$*&Nfk-_H$^ z=;!fXMyI}HXsD>^h#)_@T>9lw>DKI6`RllE4vdR8 z-AvTAGtMch9QT1NMOxWEOisky{}tncpTcjI0;R-%l&b&oHEGRFL)0^7%$%*!GH7wY zVH1bp=5*u64FWDZC2cY2P>`io1nH*NXiy?7_@5D9$xdpLu`R#nfhGP+9$535dbT-- zsN`&!WYiInU^84SVf&|2j1HES?q-hKy@Me=OxA4J91Pp#)=(0|e0kP*HUB^NSDy!kP5cYF@e~dJ@9c+vU*GP5htVG}6;V!%3;k#$cK0ZKVL2$Tuny1M%dPde14HD_tyTi3KDrL`f zbBgm73k`(8!*<(LmE=9l-v89TZSXELvpqSEyhwj4JuMAv4QIP6!;Su$yDK~a5bkQw z1w)oLnOyl7}ESsbG zLU+9@8`qB!q6=3re%M*g**7;eU7MX6AE)c@fOf|Ie!OnmEj@k14V}SUP5g?beeYnw zJO9ww4M*861)YqD{T??sfe=vVRi&k2SZVp96FsKuuB_!ZPmF` zEXJe!_Yj7u*(gRK-KT2^tkp1|PC>HQ)06u<&<=FFUxhQUo29)9Gqmmy2~<}`_IRHvq5xclTs8x|R*?75vJiy>=gRM*4022%0EW(6nX>Nk$fM%Eb(G}jO5nv0P;3~Ah{PY3Kk(9k7y$4^B%c} z57Px9M+*oDOM<4(o?7K7`$7s%YwOkJ`ElNRNoi>V5VH$i$O+l}7avlyg`o@+cKw8&sw@zIc_>0-0Jj=n28x6mFtCLCrsRUl}0ArvwZAa-q;n zZClq~i(+u)rta5$%6fz4w?S=ldt~+%Vj0HxIhZCOh*uC>Lj6Va zEKaDxB2}tRuz~u2H2Eye&!2~CB|)fG!XKep*TxQrXK_l`z8o|))%)HmEiXUsBU$tN zTNQTVt&6W}hw^pLlX6h5-P15ws`F-3DKQJQ1LEo~CLJ?9eQt7cvR_h?8X%INATC`% zpcY!?{RwYbDY6?y#^baSy+}z-MG!yNpiN=JD`bA^a4%H-sE8Wm61P+Q{Fph>&_;TJ zIBdf3H~mquFvt4dOn?RM1QCA#_&bjW1&510jfcwoy!3Y#WdIM!d6k~V#>V#bt6_~l zK~n7LdBWDSKN7Y~&%55BZlibVyp&_E^!I2k!8WV3ek!xWHW*ph3m|JNZ)h1(XU`s~ zldOFvGGAF*sz>t2gC)OOGK$uv3FhDe{i>n-48 zpl5MefE%O@w*B7X)gCIj>?-@c(}S&-3AgGpK7As5WMkGwtF7OrqUhR=i$y!W+aea#Kl{RWFBl>d=jY`yKq19T_Kb_qTH4Gi?`6|^SP|DVY%TsgoGi9n)XaU{B{LWAC*9T3(lEj(`P5&IMrT8mY>$qd*Eth@S-0Z9* zUly>Vv6zKczv>@%F`_I%1PBuqWE8xzUaf%gT11Q!I%})Qbq3D8m+O^ld-f#ed>Y!P^D~mOe&hrh= z<+7B>n7yAyMrMak@6;#LBrEFFEIccUHe1{A7`M{bU#N=%6JHu;L){3Xp&fVf%|AD| zHDpL_EY*5+07H>Hk6vEi4R@W4&KKYCyFJ#j+s4OGjJ4c6C`!F?z8rk#|AhsZAJ4Ga zZuTzr84`~Q;vsW!ae3j!usLo%ZA*d%Yl;U=km$KKls{+b!D4&|8U@b|=2&(@%AP_n z=wA&YsKR~(G)<)zb0#B?Ch}`s`6a0_tBHQ7JFAshHp9q1`3H1A6P{evTguv@e%x@D(K{jscQW zei6y}ybGgg>Szk>IjD1xs=P%))Wd^4&|R;>=r=q(Y^az1yPIRns{YByMhtj4{FcFO zbM#j`M@V=$6k^bB%K$Y1@XC9Caj2rMhN%)u)nqkXMG&rQMS5L#CK-D5Sp4u_9o~jZ z1aY_2izcLSCX;SKK>{eHh=4$xjTC_l(CaTu7@Aw~NMN+QjQ03x#PG;~;_r@f$%EM~ zK-FvR-gcvd3h+{0XE?_(?PgCH>FC~U7MP6qJi(~7LIgN;ru8yCh3$bet&B=BH>9+* zf@;-%e|)qpvgfIvy5#TMIXpD^hharUvV-qefNX_PwOd($fjrakh0l?BW+CA(HhwJh zFJV|Emp%R)Ck3xsecKP@Qz)t>A}1fHcIK%<<+3ZO>FpQ*+bP{5#^g0{Qv46l^`xxF z1;zQ0oQM373JN_!N5M<+jK}#KY4_FwHhIJ~b$I|R@#Qkcd!5Hxk-jn=;7vZHITde9 ziO=Dk|8X99C5epuh|S8<4TGKb$$4ZJ$=2}-3mNkcc*0DzUYMf}#H?RQ7X0bRs6B)j z|6hg-feTNuf*BZ9X`7cm?Y1iYo{sfhwaj{fzwZ12l^vO{6#V@Gxkw%hlhDfjSzB=( zB=bf0&u-`ain+SjRr~q#@~f!^h4X+P@Ug5jQLx!URrvOpH+FO@^?AZo%(=}0@|}LO zIb2Qvo5MSY-)#<{!cxX9i1zX>v$BJ9D@c|64H70@NIG{3FK4 z;4Jdqrc-)7TT|x&hbE^x>?Lte@P9rLUnGh!-0#F!n9jYKJA@dD<^PR*{Qk!UZB2x^HpHCGJ#XkB&QbKI*?{QTxVxgM@l9>hX^k$Y< zw6(V{q|gOI1QyzP1lBW#aEmo|IT;z!K9Uiuw3Wa1Ys>Z*^A^%mFmCMxRaEJbEb{wk zd&K=M?<$Va3GzU|7kmBH6GOt!367i)QY$ z%ABr{K4#)WU%cRF?XB zdfUqj#FpHkE(66q&ItA-w6L0+n}7XE<^6~#k8TprH4`X8}e*;P8H~K zQBpaIOx^n?o_k)qwlmK3I^icE?PRqYJp`r{Or*p84N@#&F!aTL`Fmx=D-^5ceIze* ze}WaVl8p?MB^zz?wKA*Kwg;&cPkfe4jM{i+QGJKK$v-`7sidL%%abb8M(}>BT9$)~ z8vRu#pVb7_MFac%$b%6!RV)UTED*XJz51LeseY{<;<`D}G26cuMI?RwTTw)F%>T3~ z0`ET-Mf?Jx(-Wv|l*=4aJl%TVsRUapA&Ro$lh?tb*2Aauk}5cUjsgZ{ z>+0KR1RHfxZa;_wS27`HEs^-fu^Z`YHwH(e$3r%byE+3zk?-ByH;qcu4*X zI@>tBp6d2S6V!__09%N21(Y81a~G%Qu~OaF4P#-wry(iQ;-;Tbd9iiBH2}rj)I3B( z1*IKzg96ycXmO3b^1VziZ(H;IC;Jbw17U?f6&LrsFji>DdL`f8lMqb>r_P=tlGj5M z-ido8d{52;>v&Edt+oy2L;3J^oj+@=?LMzV*9EmA!9Aw@=^f^NXU;29IW`IQ@PhxVQtqBe*x;t;jAc78<6B$04Ccv)HWj*7%WRUQ@6=b7I<| z6cmLk^UY)cbC9mXK2{Z-#ZFMTeYUqm(?^o_T}So$NK z-(Y?Er)2*3Nw$yTPieH0lFoA`XPZoZ~SHO{{2h81yB7NlHp5z z^#9$@|8IIIg2_PDKLlMWAa}c;P5?Ns^BI7PN@ah6bSMA+R4XH5k^QeV>^&fe>$0^r z!YH>{V2Jq&zFBCQcXt=ktZ?*xbZEDW%iPl1(4g@2DH{#VD$pm@XQzl67q@pWrjVsT zS9}Hqn9m9K;iTdVg?3^E0p%F!*6lABZiQ#TJzNE=z$YH>&t3;e>>y(PoX4(QY+V>y zD5JfmP|C0m{{w<|faf6OHhNiZErx)A0IM4=ZqVY8mGDP+{viQyRsZ+4h64nue+-rq z%=pj2QkY3@h$dWR(3|V`?wuOCt;C7?E@9%Wq}%x$l$J8!@G z4du;LzzK5-t!cAhjfpUnnWjK!|no%KL7$>5K2X*XwQ&C(^LWJo99 zsLl6g1gq~s8&x^s-4phe!uRjRLf1Zy=KXpB9Ld2zfg_cF?HEh4vpx|hKMO2vl?`W$ zmM=U*{&Ax1R#3y@s^!x%?@Q%QxvKV9OH2B{MtaM}u~6}t%(Oery`)D^=XTwF!)@|y zZAxmHAW*X0+W24=fT)W-K!16n;n(1&-Y|wfWunKRy=`7!PZGS-lZF@)8v6VL=ME5h zZundke+iZHD%da~%rP3e4idES@zC3;A&FpED>Ue1AS2uHAF@CN-n3z-@vh7iyyT;<-%zlvak1u)#b{bAL0^{ z$W^EPF=Q_w!TaU0umO?FnJB_Qgj)q20@p}Eg?QT1IGs_Q8sogdgGI;v(`WbgIkYW7;#qdevd-&Zg+R@ zjaGp#U%!ri`7%D{oZVFAlOK#dFB^~x^^Z@Tc0{_rG&MCfchQqEUv-<>B%x3uEF*2g82#d_fa8J1!-kQ1HG&JUXmXxJ30&$*S3n{6k_!*RxqD@DtVq#-2 z9)-z<9oQa>lS<$V9ejcatd3aj%L7b?{Woa#ChJj1qU(62M1z~Juq-5e@rK;CoSr-( zU#R}YyM1)b#z~|RW^_#U_QFIff`?l;Z5-FpG!FW6#&$%T&W8`%`g(h_luAXW)6BW= za@mCx6+s5Qy*DQmFuONS`5lf$0N<3)Bic~-F{0y|E79-2n~Gf=s2nZwkV7nxeU{%* zQA=&UGenuRjiSa&SE`%RnhBp@Sl=#?Vb~O`E_-!?+;lTRG~6+@8S_(2xHql3Vs>n< zo*GaMe1=}%bCkk zQJPpxswV=%Au+Kg-@QwIZM7rH-KQ4&D0O!ODB7vp_{=xAj4^O)9GDGy_3ROkay6ZL zAxXU!P`BO1k??Dw+?{5X6<2B(CvpJgVc9+SpxPhHiA#o;UtT`khTEg8jNjRFkvk4U z#6)XaqCsb&o6M=I%4}+zj**d($x?pbzz#Fo_5!%$M5je3)&Ia^YH5EpuRdWOJo;`?zqY1N_NvZU9bFBTJ{RSa>)G zDdA4fC{dhcn&6uSF^Y|b%Jg162yCj|z`l=$#}sdyufv3S2a2%k*M#4bY`$QNL&xR3 zO|v(b)+5q{kT=rs9dGeUDO1a4R!V@wW6UdjzyGd6n+~QBl?jp2lmcyOy$5^Z2=S1V zq;Mye_@8XKKOU-`u_yA5TXjZ-LhSQ8~ zWoc=>GRkE*i|Wjy+GS;4cj2Pml!p}%Ir08a?#5E!b8sjQHq_Vm)apy6^=3#RN{41a zj`zZy2l{7h$J$0(SB86gr3YiVUemDBimQru)b`xHQE9z0ytC4CG?+Wpb#}ZoKfb;w zAl#(NYJ34{bGBWPTG$2n&TjcfF4}e;)Y^=eykg`c ztu5rc?Ce}UIOu;e(w}*B)np51Z2)bY4woZ?gQuXRT{cT8HY^sM|9as9HwOoEO8bkdix!ubT*?snDX*EutBOZIDBgbPgDhNy#~T#z zY6{GnkhB>NKsdQ@V}gJuZx90mGS*f^pT9ORTLELqF6F3D-?`JkB?Tl?6)?z z297I`x3wRqhQ!4B;X9}3sHms}MB=lU1eLj=2WAhkoL(R*>pO=?Uj&}KDzPP;|&}yR~-cj&r(f##m zV@?nA%F`Go-pi5m6`$(Mt{spKHjq>hB~1(W984RW)M%LyTkjji?cf2dq` zrvPa)Q%y_j3SM1qlIPl3dA{`uo{UT=g;W}}c6-sUubV+0k51YBu8RYwPE{HkX3|%J zR=;{(3owd~iB48&g-D$u64>9hMO!450`1mG#kabcwZoCYG~lGSZeb#xZ$m)6Fv^w%;@xJFZ|)2 zv|R5NdzXJF6R*_q=>5EdFA;4Gu~uG6ihJ@anPYgh1zTn8%GB(qW}gr0B%?6Mii>BP zFHm5xaLW({=sZrk$6t-Dj}flnB9bKJJ1efC5pnwQVFHz1M>IV(Il1ogAf_Qcm_w?4 zef`(Ek~o@L%$1i=<`{n+nAaq0Gui^g`{pQ=?2|M&NWWETQK)Y1w?s}RMx-;ze{2cl z^hTiE5N#?(Z!$CS!|!qAjvMa-!i`R)Y25(N=A%1*np~(sva}m$k4|c8f__Zp0 zZrc{7X4A`;IYvq?3FCu+cIr`>9mbj<{-~zK8_@a-Yyp93xPdV54}33=a-IVq*h36Ma=k zUjCrZP@bw)Q68yb3VO*&Ue-E zNkq7m%=5xuPd@vetg-9fMM_GUyZ*FMSfn1H<6Rj!VhzOpkQ2pfO7BPxM=O)j;OjK; zn0=EvvkY6A(ZVXTziea7ci8Nux-CjLLkEWH!)n~QMe3_%C?E7!-J#l*czQ&~UlBph zmKWu4dzHE{X@(WS>wZ7W?)p%!<}J_Kh3|#F5qw9x+&O6Ic*q8KPu|>5hcHw9r0}eAfSDK}Zu!_Ea|K3pFsEXM&3o);D+w%nD zYvg5@m}|qRoevHU(eJWH$fLTcmz!a3z=mB@ivbCfuQw8}f_a2=lh<VAiP5nq=&>!yG z{Td**uW(=S!BKBx3Sqd0!E+%lKA!k8BI@nVYE^7;jI%dl%f^FBR5QvxeuagB34!yL zKIrJX$qE@%x#QkdRlGWh*jK&IAEWw!LYJwi=+nL)QhXNf6A>g_R#|A0-;1pjbQ%rI zA4SJeB)jzm?t^20U4!Nlk)t6O$Qf1HNQ;~LdA-TU=!`>)T~7@;lfG+AI9$FfS@&r= zk5iTUlWe93!tt>!;S3KZjXHkS*_Xungf`vDMYEN3n6Tf>vv|PKnxqAb}lL;CY>*_dh zzE?b?q-3C{55)CX+c*0ZMvY`xWI8rPk<$uAw=2ubE%<4)eXwh)RAZ$FXOZ-1TtZV; zep<-yY2>Rxx-2eS`2~UHRMN&y#4V{FdTXBwfE<+uLtEsoU zqJl!VJRariOjCw9tUnH;()8qKPp>dar(UPFigl`AITrusBThZD<$LFyC=-tux!k;g zi1ee6w31{i!-ZS8tk$RpkB9R2fEp*@kBY|a9H~;~&|4(rdBxwxzOw_r%64i)gx>9&9;TK^hqNOzq{M`5*y@wNiK0{r#nMI`t%B ze+K%Djw@m@3`0##^Kgk=M7Y-}udI~A{beIjKvZ4GIvv?JxO-+uNGRy2>Vd;o{1X5;BK6-*RQ>^LsplhbmnBFI&F#bVGK zAiwWBQ#jo+x7q#nkN41lF$C>!cEKR5xBr2R97W&t&&x_inH|om>C$ylj>@aa`UcIB zN^;mF1-kwW;c2JiGJ#P-DvH9P+jq!l2(wyQvob?Mvo{2NvNo1d(O}>O@q*z%Cixf% zRG)BQR+;X8#tjSloVPpG;ZGgYl@HnL(aljT!2wKUh`ON4}kFZMuwVsdDepS&@*E0?eHe8tGv zYH6{oyc~;yA-B_{mbL6n2b6)GO#?U*@0X>&a)Ymlg;m&OPhr66PJd>RXlZlh>?{Yli;Fz0ht$bJR;!@>b{93uPOea-Ga1g_!Wgwv0K*Lu@?dpyy2euF2(&HZt9$7;Dd(KFy zVe#^{fy^Tz2-;CpgebGn>#CWmmF&yw374JED%TFSZk-$NAClDQinK}Y!UEF8UHCcV z!Khb-)}keXc}Mi!B3(kgb%Ukw!ZLQ~vg@Xptgkqgfttv)AQufyMfv z7x$9)mYUn!`+9m>+gk38J%Hr)v0@&F@i(VHR6>f98S?E$Xy^Yns`7Z9=RDDmraYt=5$P)VmBe`|IjVS2$~$LX3`Oh+)$P zAO8cqOB!T-R4!O!ej&Lm(qw82QccbQZ?Lbc$Ud4f0J6KuRYR0T;ynRJY|saG=g~V& zTwPqdGX5;yPAb!cb^T+b9m zT>jGNa4HxYF12=bF&G(qsD{|&r9Q^fxj1L-!H9_7V!GmVLWr^n<}}!dm10`lIjHw+ODNh~1;ESI@B}>bCZ^ z5oe;c+s5>R`=TxjV9PP-?JUd*pemHk-y2{>YSk?@j$+AHsn)r9@za%uTexR8|J=j2 zq|=^x6EzL^raydQaDkbc>yNqfnroB0gQn1*r^Ro_#trnAkiUEj{2*z@2T~+|`;uh) z_>X`4ropJ|f3AFZfB^;w+9H3PM8EyvBW?)A-~V$NIxkWM|9INxe@Ono7x};XK_N)4 zMBVe9#6%<{4!szp&2tw?REt+{xi&RJ^3dzm!P%9Ou-@C>3%3r8w^HX@ZxM1;yx#Bp z+La{ijr)MIUB(-+b!$Kr?cvrKDhMZESPJp*Kx7v;&%D&ne?*{&ylD%+oRpI?b#pfJ z>OFJw(i|x*%@;-r;SaFdKW(%KaW=$%8L1Ey7C170$kk@3NxQ=k-NxzXnH?$MbQdiv z|LIfZOi7vd<|f4CWO6IEKIZE52mLPhF-Io0_7@fwvd1chz&|t9=%0c&R{gqMHdDd( zrl-kJo($6+!GJdxoFx_5b&Q%o1Q`M2cFkww&vv`ZwSz`Kau%Tf{hf<%{#Fnf%GMSN z0tOPvW+z)t4^(xY=ll0DYiMYI3Uljd%{i#!LNE`niI@pM#7Ul@!V_@1+-@OY@4TH2Ag8aX=w^!P1T+g@&1vp0EqU9a9&)TC{J2W%_Ey#RGU2k+F zboOK2R%n=R7kh%2Vo1=3Lxzc&_c7%FEeM}7_7*Vks5^KSzm5WAkVdJ%&0(ejsy>>7 zZ|_4^oNT7u&LRvc2DO$-=-*WwnP{8!fyAIh=>s3QC8Cu z)jJrxNX$f^AVVnhF-8^Ypv_1k%h5&u`uM{S3S@C{j_erhY!%}fr7RK2#~Eb?xo=-N z8FFkAUQpQlCMK4Xl#;SA(`WF~tP^JKx%Mbh{e`tT6g2X|4dXGfWhxL!dNsDU8^1Km z(JKiKzU=1veSTrd8_(!R3Bfm?N0t#ywec>OVVhi9UbdWSXop}q3yb_n##GVg7JV*E zU}^7m#K(`icydIoQnCJR1;5R)`D!vFH%&jln0!?TFAqUU%LVOeBgH(?y{I0jxTwQ? zZ#|3jhtXB8UqP^rVR1~2@JsAg>RCNO`O+Kt#>K@60@@nh;W{o%ve~@mhevv+T0HW& zXGL+~++^r167O?gm4|k6QU}VFOn&T-tCRL0=Y>f}W)~0${QhPI;PK0wiWG(~T;X<` zE-a#^|42G77%8=%k#hZ5&S|fzsezYk*6-czP-LITZ=sSDMkZ(s={lC54GgmAh$zNd z-+FDp&5p=}KB_%f7{+iIP#pfpSHN|(1xEl6;z>OhnZr}OCBK+a*+vxVx}>M{F(dWn(dygB6ueW9`Ay;tbNyVTBlDhOcfc$h zXo`i225X=1Eri=$52mQB9EOn#94p+Aiw0Y_54wY_F!X*AQ3XceUguA8 zx{ZrDPDmI>_o`cq1S0?yB;cgNB)B-KiUV0dEavp&>tX;jq;Ue}r+5JrD=R6MMOLq0 z_w@I__xE?+5fRCK^~$;^A3M4)PYC5jpq|?D)?5HmPy;OM&LX#Wj>r2?H$RC#K1O0z zzbGpk7Jw6v9&Ap>z+i4}`RwWQi?8HAy8L26Dy^1SGVt0zFt>MfK>i8Vvb~)h)Zd*Q zt>J_%xM-D>JnGp*c&(+bo|T$v4ax5$bBg8m9bb{z`i$@JrM9st^fa z%?yYJI<{fHaD`&o*f#3?Qa{qZ@@=GiGpu&A#p5=^;uR710|ClSA$JxZN*+|BMKs*w z(<4Hwokid>QC5{D7_{9FlwzyD7n?!=KI5aSCX`%UQ=rfneCelWpwDnMXtpDBxcKXZ zUFyt-TU(n6Z2^=-!)wg>1e#LlKM$c~527)VvFOj2W};n5IHp8eLpZ0tyV{KE_##l= z+IS>?KxW6z$z>e+j>DsH6AfaiQ(j{jLJzp**Xu$rqax>%DKR*Jk>Vw_lL7%?%FD z89K?^Z)$~9?9oKYGwNqHW;?1{OVlERhWM~69` z@%aaqTZI|=Go|KlN-oQ>7Ska%duy|O&M|s1*o%ZSi=jT}i)?&D#_3wQT@pXs{={BG zm&ud9bh&cq7YGJ2&CI-s>m3A-!0Zb-|qB z!88FBqL$3fX$)jBae*e)PajfW`D6?boiBp*a_7aOxo>fZK(-_`cJ2pqLQt{p!0HH5 z`2Nv>PgXD~0+S7k$;&i|{4yr?@@evpiBU^!f(_K}k@4b<7h3UFy@eptin&a-g0q?p zRqM4Zl#5K*i3wR7rzb~{vdjO`$CgtZV-K(*NHl^^0=@DpNT)hunPEDHo~`q=D0THA zR0KGeG$%K@OWG*v; z&PHx$6H{Hp)$JW)9?gaVSjYMA=0NUPYHNFFXGajb+G%GxP^CP^UFm<9gW#m28UlZ^ zxh>yGhE((O-=FXAbDy01jl!X3c6J&mxH#<(+j-0*+|a

  • II(xX^_Algv7)lxqU8 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

    6mHnR&oj6Zs00nI-Ws4StA-8X((TCisU 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 Z49Zk*vx5o2rXU0aIVoky(&xs3{{<(1 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 B0T=)P literal 0 HcmV?d00001 diff --git a/benchmarks/api_server/launch_gke_server.sh.template b/benchmarks/api_server/launch_gke_server.sh.template new file mode 100644 index 0000000000..fedb5d8dd8 --- /dev/null +++ b/benchmarks/api_server/launch_gke_server.sh.template @@ -0,0 +1,78 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +#!/bin/bash +set -e + +# ============================================================================== +# 1. User-Configurable Variables +# ============================================================================== + +# -- GKE Cluster Configuration -- +# (, , ) +export CLUSTER="" +export DEVICE_TYPE="v5p-16" +export PROJECT="" +export ZONE="" + +# -- XPK Workload Configuration -- +# (, ) +export RUNNAME="my-server-$(date +%Y-%m-%d-%H-%M-%S)" +export DOCKER_IMAGE="gcr.io/tpu-prod-env-multipod/maxtext_jax_nightly:" +export HF_TOKEN="" # Optional: if your tokenizer is private + +# -- Model Configuration -- +# IMPORTANT: Replace these with your model's details. +# (, , ) +export MODEL_NAME="qwen3-30b-a3b" +export TOKENIZER_PATH="Qwen/Qwen3-30B-A3B-Thinking-2507" +export LOAD_PARAMETERS_PATH="" +export PER_DEVICE_BATCH_SIZE=4 +# Parallelism settings should match the number of chips on your device. +# For a v5p-16 (8 chips), the product of parallelism values should be 8. +export ICI_TENSOR_PARALLELISM=4 +export ICI_EXPERT_PARALLELISM=2 + +# ============================================================================== +# 2. Define the Command to Run on the Cluster +# ============================================================================== +# This command installs dependencies and then starts the server. +CMD="export HF_TOKEN=${HF_TOKEN} && \ + pip install --upgrade pip && \ + pip install -r benchmarks/api_server/requirements.txt && \ + bash benchmarks/api_server/start_server.sh \ + MaxText/configs/base.yml \ + model_name=\"${MODEL_NAME}\" \ + tokenizer_path=\"${TOKENIZER_PATH}\" \ + load_parameters_path=\"${LOAD_PARAMETERS_PATH}\" \ + per_device_batch_size=${PER_DEVICE_BATCH_SIZE} \ + ici_tensor_parallelism=${ICI_TENSOR_PARALLELISM} \ + ici_expert_parallelism=${ICI_EXPERT_PARALLELISM} \ + tokenizer_type=\"huggingface\" \ + return_log_prob=True" + +# ============================================================================== +# 3. Launch the Workload +# ============================================================================== +echo "Launching workload ${RUNNAME}..." +xpk workload create --workload "${RUNNAME}" \ + --base-docker-image "${DOCKER_IMAGE}" \ + --command "${CMD}" \ + --num-slices=1 \ + --cluster "${CLUSTER}" --device-type "${DEVICE_TYPE}" --project "${PROJECT}" --zone "${ZONE}" + +echo "Workload ${RUNNAME} created." +echo "Use the following command to connect:" +echo "bash benchmarks/api_server/port_forward_xpk.sh job_name=${RUNNAME} project=${PROJECT} zone=${ZONE} cluster=${CLUSTER}" diff --git a/benchmarks/api_server/maxtext_generator.py b/benchmarks/api_server/maxtext_generator.py new file mode 100644 index 0000000000..c81d425fbc --- /dev/null +++ b/benchmarks/api_server/maxtext_generator.py @@ -0,0 +1,634 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may not obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import time +import uuid +import json +import datetime +from typing import Sequence, Optional, List, Union +import logging + + +import jax +import jax.numpy as jnp +from absl import app +import numpy as np + +from MaxText import max_utils, maxengine, pyconfig, multimodal_utils, max_logging + +from dataclasses import dataclass, field + +# Set TF log level to avoid verbose startup messages. +os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" + + +@dataclass +class LogProbs: + """ + A dataclass to store detailed log probability information for a sequence of tokens. + + Attributes: + tokens: A list of token IDs. + token_logprobs: A list of log probabilities corresponding to each token. + top_logprobs: An optional list of dictionaries, where each dictionary maps + surrounding tokens to their log probabilities at each position. + Currently unused and set to None. + text_offset: A list of character offsets for each token in the generated text. + """ + tokens: List[int] + token_logprobs: List[float] + top_logprobs: Optional[List[None]] = None + text_offset: List[int] = field(default_factory=list) + + +@dataclass +class Completion: + """ + Represents a single completed generation from the model. + + Attributes: + index: The index of this completion in the batch. + text: The generated text. + tokens: The list of token IDs that make up the generated text. + logprobs: An optional `LogProbs` object containing detailed log probability info. + finish_reason: The reason the generation finished (e.g., 'stop', 'length'). + prompt_token_count: The number of tokens in the input prompt. + completion_token_count: The number of tokens in the generated completion. + """ + index: int + text: str + tokens: List[int] + logprobs: Optional[LogProbs] + finish_reason: str = "stop" + prompt_token_count: int = 0 + completion_token_count: int = 0 + + +@dataclass +class GenerationStream: + """Holds the state for a single generation stream within a batch.""" + # Input state + tokens: np.ndarray + true_length: int + image: Optional[np.ndarray] + + # Output accumulators + generated_ids: List[int] = field(default_factory=list) + generated_logprobs: List[float] = field(default_factory=list) + + # For echo=True + prompt_ids: List[int] = field(default_factory=list) + prompt_logprobs: List[float] = field(default_factory=list) + + # Status + finished: bool = False + finish_reason: str = "length" + + +class MaxTextGenerator: + """A reusable class for parallel text generation using MaxText.""" + + def __init__(self, argv: Sequence[str]): + """ + Initializes the MaxText model, tokenizer, and engine. + + Args: + argv: Command-line arguments for MaxText configuration. + """ + start_time = time.time() + + argv_list = list(argv) + + # Check for HF_TOKEN env var and inject as a pyconfig argument if not already present. + hf_token = os.environ.get("HF_TOKEN") + if hf_token and not any("hf_access_token" in arg for arg in argv_list): + max_logging.log("Found HF_TOKEN environment variable. Adding to config.") + argv_list.append(f"hf_access_token={hf_token}") + + # CRITICAL: Initialize the distributed system and config FIRST. + # This call to pyconfig.initialize() contains jax.distributed.initialize() + # and must happen before any other JAX calls. + self.config = pyconfig.initialize(argv_list) + jax.config.update("jax_default_prng_impl", "unsafe_rbg") + + # Now that JAX is initialized, we can set up logging and use JAX functions. + self.rank = jax.process_index() + self.logger = logging.getLogger(__name__) + self.logger.info(f"Initializing MaxTextGenerator with argv: {argv_list}") + + self._validate_config(self.config) + self.logger.info("System information:") + # Temporarily redirect stdout to capture print output for the log + from io import StringIO + import sys + old_stdout = sys.stdout + sys.stdout = captured_stdout = StringIO() + max_utils.print_system_information() + sys.stdout = old_stdout + self.logger.info(captured_stdout.getvalue()) + + self.engine = maxengine.MaxEngine(self.config) + self.rng = jax.random.PRNGKey(1234) + + self.logger.info("Loading model parameters...") + self.rng, rng_load_params = jax.random.split(self.rng) + self.params = self.engine.load_params(rng=rng_load_params) + self.logger.info("Model parameters loaded.") + + self.metadata = self.engine.get_tokenizer() + self.tokenizer = self.engine.build_tokenizer(self.metadata) + eos_id = self.tokenizer.eos_id + if not isinstance(eos_id, list): + eos_id = [eos_id] + self.eos_ids = eos_id + try: + self.has_chat_template = getattr(self.tokenizer.tokenizer, "chat_template", False) + except AttributeError: + self.has_chat_template = False + + self.logger.info(f"Chat Template available: {self.has_chat_template}") + + self.batch_size = int(self.config.per_device_batch_size * jax.device_count()) + + self.rng, rng_init_decode = jax.random.split(self.rng) + self.decode_state = self.engine.init_decode_state(rng=rng_init_decode) + self._jitted_reset_state = jax.jit( + lambda state: jax.tree_util.tree_map(lambda x: jnp.zeros_like(x), state) + ) + + end_time = time.time() + self.logger.info(f"Initialization complete in {end_time - start_time:.2f} seconds. Max batch size: {self.batch_size}") + + + def generate_batch( + self, + prompts: List[str], + image_paths: Optional[List[Optional[str]]] = None, + max_tokens: int = None, + logprobs: int = None, + echo: bool = False, + stop: Optional[Union[str, List[str]]] = None, + temperature: Optional[float] = None, + seed: Optional[int] = None, + top_k: Optional[int] = None, + top_p: Optional[float] = None, + ) -> List[Completion]: + """ + Generates text for a batch of prompts, handling chunking automatically. + + Args: + prompts: A list of prompt strings. + image_paths: An optional list of image paths, one for each prompt. + max_tokens: The maximum number of tokens to generate. + logprobs: The number of top log probabilities to return for each token. + echo: Whether to include the prompt in the generated text. + stop: An optional list of stop sequences. + temperature: An optional temperature for sampling. + seed: An optional seed for deterministic sampling. + top_k: An optional integer for top-k sampling. + top_p: An optional float for nucleus sampling. + + Returns: + A list of generated Completion, corresponding to the input prompts. + """ + if image_paths is None: + image_paths = [None] * len(prompts) + if len(prompts) != len(image_paths): + raise ValueError("The number of prompts must equal the number of image paths.") + + all_results = [] + num_prompts = len(prompts) + for i in range(0, num_prompts, self.batch_size): + prompt_chunk = prompts[i : i + self.batch_size] + image_chunk = image_paths[i : i + self.batch_size] + + chunk_count = (i // self.batch_size) + 1 + total_chunks = (num_prompts + self.batch_size - 1) // self.batch_size + + chunk_results = self._process_chunk( + prompt_chunk, image_chunk, max_tokens, logprobs, echo, stop, temperature, seed, top_k, top_p + ) + all_results.extend(chunk_results) + + return all_results + + + def _process_chunk( + self, + prompts: List[str], + image_paths: List[Optional[str]], + max_tokens: int, + logprobs: int = None, + echo: bool = False, + stop: Optional[Union[str, List[str]]] = None, + temperature: Optional[float] = None, + seed: Optional[int] = None, + top_k: Optional[int] = None, + top_p: Optional[float] = None, + ) -> List[Completion]: + """Orchestrates the generation process for a single chunk of prompts.""" + start_time = time.time() + + # for prompt in prompts: + # self.logger.debug(f"Processing prompt: {prompt}") + + initialize_start_time = time.time() + # Reset the state to handle the new batch while reusing memory. + self.decode_state = self._jitted_reset_state(self.decode_state) + streams, rng = self._initialize_streams_and_state(prompts, image_paths, seed) + initialize_end_time = time.time() + self.logger.info(f"Initialize step took {initialize_end_time - initialize_start_time:.2f}s.") + + if max_tokens is not None and max_tokens <= 0: + self.logger.warning("max_tokens <= 0, returning empty completions.") + return [Completion(index=i, text="", tokens=[], logprobs=None) for i in range(len(streams))] + + prefill_start_time = time.time() + self.decode_state, rng = self._run_prefill_step(streams, self.decode_state, rng, logprobs, echo, temperature, top_k, top_p) + prefill_end_time = time.time() + self.logger.info(f"Prefill step took {prefill_end_time - prefill_start_time:.2f}s.") + + generation_start_time = time.time() + self.decode_state = self._run_generation_loop(streams, self.decode_state, rng, max_tokens, stop, temperature, top_k, top_p) + generation_end_time = time.time() + self.logger.info(f"Generation loop took {generation_end_time - generation_start_time:.2f}s.") + + completions_start_time = time.time() + completions = self._build_completions(streams, logprobs, echo) + completions_end_time = time.time() + self.logger.info(f"Completions loop took {completions_end_time - completions_start_time:.2f}s.") + + end_time = time.time() + self.logger.info(f"Processed {len(prompts)} prompts in {end_time - start_time:.2f}s.") + return completions + + + def _initialize_streams_and_state(self, prompts, image_paths, seed): + """Tokenizes inputs, sets up stream objects, and initializes the decode state.""" + prefill_length = getattr(self.config, "max_prefill_predict_length", 1024) + streams = [] + for prompt, image_path in zip(prompts, image_paths): + toks, tlen, imgs = self._preprocess_inputs(prompt, prefill_length, image_path) + assert tlen <= prefill_length, f"Input token length {tlen} is > {prefill_length}" + streams.append(GenerationStream(tokens=toks, true_length=tlen, image=imgs)) + + if seed is not None: + rng = jax.random.PRNGKey(seed) + else: + self.rng, rng = jax.random.split(self.rng) + + return streams, rng + + + def _determine_sampling_algorithm(self, temperature, top_k, top_p): + """Determines the sampling algorithm based on user-provided parameters.""" + if temperature == 0.0: + return "greedy" + if top_k is not None and top_p is not None: + return "composite" + if top_k is not None: + return "topk" + if top_p is not None: + return "nucleus" + if temperature is not None: + return "weighted" + # If no specific parameters are provided, return None to let the + # engine use its default configured `decode_sampling_strategy`. + return None + + + def _run_prefill_step(self, streams, decode_state, rng, logprobs, echo, temperature, top_k, top_p): + """Runs the prefill step for each stream and inserts results into the decode state.""" + sampling_algorithm = self._determine_sampling_algorithm(temperature, top_k, top_p) + prefill_results_to_insert = {} + + for i, stream in enumerate(streams): + rng, rng_prefill = jax.random.split(rng) + want_prompt_logp = logprobs is not None and echo + + prefill_result, _ = self.engine.prefill( + params=self.params, + padded_tokens=stream.tokens, + true_length=stream.true_length, + images=stream.image, + rng=rng_prefill, + slot=i, + return_prompt_logp=want_prompt_logp, + temperature=temperature, + algorithm=sampling_algorithm, + topk=top_k, + nucleus_topp=top_p, + ) + prefill_results_to_insert[i] = prefill_result + + p_ids = list(map(int, np.array(stream.tokens[:stream.true_length], dtype=np.int32).tolist())) + stream.prompt_ids.extend(p_ids) + if prefill_result.get("prompt_logp") is not None: + p_logp_arr = np.array(prefill_result["prompt_logp"])[0, :stream.true_length] + stream.prompt_logprobs.extend([float(x) for x in p_logp_arr.tolist()]) + + first_token_id = int(np.array(prefill_result["tokens"])[0, 0]) + stream.generated_ids.append(first_token_id) + if prefill_result.get("token_logp") is not None: + first_logp = float(np.array(prefill_result["token_logp"])[0, 0]) + stream.generated_logprobs.append(first_logp) + + for slot_idx, result in prefill_results_to_insert.items(): + decode_state = self.engine.insert(prefix=result, decode_state=decode_state, slot=slot_idx) + + return decode_state, rng + + + def _run_generation_loop(self, streams, decode_state, rng, max_tokens, stop, temperature, top_k, top_p): + """Runs the autoregressive generation loop.""" + target_length = getattr(self.config, "max_target_length", 2048) + prefill_length = getattr(self.config, "max_prefill_predict_length", 1024) + sampling_algorithm = self._determine_sampling_algorithm(temperature, top_k, top_p) + + stop_sequences = [] + max_stop_seq_len_tokens = 0 + if stop: + # Ensure stop is a list of non-empty strings + stop_sequences = [s for s in ([stop] if isinstance(stop, str) else stop) if s] + if stop_sequences: + # Calculate the max token length for any stop sequence to define a lookback window. + for seq in stop_sequences: + # Use the underlying tokenizer here to avoid potential errors with the wrapper + # on single-token sequences, as this is a safe, internal calculation. + token_ids = self.tokenizer.tokenizer.encode(seq, add_special_tokens=False) + max_stop_seq_len_tokens = max(max_stop_seq_len_tokens, len(token_ids)) + + total_steps = target_length - prefill_length + if max_tokens is not None: + total_steps = min(total_steps, max_tokens - 1) # -1 for the token from prefill + + for step in range(total_steps): + self.logger.debug(f"Generation step {step + 1}/{total_steps}") + active_streams = [(i, s) for i, s in enumerate(streams) if not s.finished] + if not active_streams: + self.logger.info("All streams finished. Breaking generation loop.") + break + + rng, rng_generate = jax.random.split(rng) + decode_state, _ = self.engine.generate( + self.params, + decode_state, + rng=rng_generate, + temperature=temperature, + algorithm=sampling_algorithm, + topk=top_k, + nucleus_topp=top_p, + ) + + state_tokens = np.array(decode_state["tokens"]) + state_logp_np = None + if (logp := decode_state.get("token_logp")) is not None: + state_logp_np = np.array(logp) + + for slot_idx, stream in active_streams: + tok_id = int(state_tokens[slot_idx, 0]) + stream.generated_ids.append(tok_id) + if state_logp_np is not None: + stream.generated_logprobs.append(float(state_logp_np[slot_idx, 0])) + + # Check for finish conditions + current_len = stream.true_length + 1 + step + is_max_len = current_len >= target_length + is_eos = tok_id in self.eos_ids + stop_sequence_found = False + + if stop_sequences: + # Define a lookback window for decoding that is slightly larger + # than the longest stop sequence in tokens. + lookback_window = max_stop_seq_len_tokens + 2 + start_index = max(0, len(stream.generated_ids) - lookback_window) + trailing_ids = stream.generated_ids[start_index:] + + if trailing_ids: + # Use the standard jetstream wrapper for decoding as requested. + trailing_text = self.tokenizer.decode([int(tid) for tid in trailing_ids]) + for stop_seq in stop_sequences: + # Use 'in' for a more robust check. + if stop_seq in trailing_text: + stop_sequence_found = True + break + + if is_max_len or is_eos or stop_sequence_found: + stream.finished = True + if is_eos or stop_sequence_found: + stream.finish_reason = "stop" + if getattr(self.config, "attention", "") == "paged": + self.engine.release_pages(slot=slot_idx) + + return decode_state + + + def _build_completions(self, streams, logprobs, echo): + """Builds the final Completion objects from the generated stream states.""" + completions = [] + for i, stream in enumerate(streams): + gen_ids_for_text = stream.generated_ids[:] + gen_logps_for_text = stream.generated_logprobs[:] + + if gen_ids_for_text and gen_ids_for_text[-1] in self.eos_ids: + gen_ids_for_text = gen_ids_for_text[:-1] + if len(gen_logps_for_text) >= len(stream.generated_ids): + gen_logps_for_text = gen_logps_for_text[:-1] + + tokens_for_text = stream.prompt_ids + gen_ids_for_text if echo else gen_ids_for_text + logps_for_text = stream.prompt_logprobs + gen_logps_for_text if echo else gen_logps_for_text + + text = self.tokenizer.decode(tokens_for_text) + offsets = self._token_offsets(tokens_for_text, 0) + + lp_payload = None + if logprobs is not None: + if len(tokens_for_text) != len(logps_for_text): + self.logger.warning(f"[warn] Mismatched token/logprob lengths for stream {i}. No logprobs returned.") + else: + lp_payload = LogProbs( + tokens=tokens_for_text, + token_logprobs=logps_for_text, + top_logprobs=None, + text_offset=offsets, + ) + + completions.append( + Completion( + index=i, + text=text, + tokens=tokens_for_text, + logprobs=lp_payload, + finish_reason=stream.finish_reason, + prompt_token_count=len(stream.prompt_ids), + completion_token_count=len(gen_ids_for_text), + ) + ) + return completions + + + def _preprocess_inputs(self, text, prefill_length, image_path): + """Helper to preprocess a single text and optional image input.""" + processor_output = multimodal_utils.PreprocessorOutput() + images = None + if self.config.use_multimodal and image_path: + text = multimodal_utils.reformat_prompt( + text, image_placeholder=self.config.image_placeholder, model_name=self.config.model_name + ) + loaded_images = multimodal_utils.load_image_from_path(image_path) + processor_output = multimodal_utils.pre_process_image(loaded_images, model_name=self.config.model_name) + prefill_length -= multimodal_utils.get_image_offsets( + self.config.model_name, processor_output=processor_output + ) + images = processor_output.pixel_values + + tokens, true_length = self.tokenizer.encode(text, is_bos=not self.has_chat_template, prefill_lengths=[prefill_length]) + if self.config.use_multimodal and image_path: + tokens = multimodal_utils.prepare_text_for_image_fusion(tokens, model_name=self.config.model_name, processor_output=processor_output) + true_length += multimodal_utils.get_image_offsets(self.config.model_name, processor_output=processor_output) + + return tokens, true_length, images + + + def _validate_config(self, config): + """Validates configuration.""" + assert config.load_full_state_path == "", "Decode doesn't operate on full states! Convert to parameter checkpoint first. Using generate_param_only_checkpoint." + assert config.quantization != "fp8", "fp8 on NVIDIA GPUs is not supported in decode.py yet" + assert config.quantization != "nanoo_fp8", "NANOO fp8 on AMD MI300/MI325 GPUs is not supported in decode.py yet" + assert config.per_device_batch_size * jax.device_count() >= 1, "Total batch size must be at least 1." + + + def _token_offsets(self, token_ids: List[int], start: int = 0) -> List[int]: + """ + Compute char offsets by decoding cumulatively so context-dependent + whitespace/normalization is handled correctly (SentencePiece/LLaMA quirk). + """ + offsets: List[int] = [] + pos = start + decoded_so_far = "" + prefix_ids: List[int] = [] + for tid in token_ids: + offsets.append(pos) + prefix_ids.append(int(tid)) + new_text = self.tokenizer.decode(prefix_ids) + piece_len = len(new_text) - len(decoded_so_far) + # Guard for weird edge cases; shouldn't happen but better safe: + if piece_len < 0: + piece_len = len(self.tokenizer.decode([int(tid)])) + pos += piece_len + decoded_so_far = new_text + return offsets + + +if __name__ == "__main__": + import sys + import time + + + def dump_completion(i, comp): + """ + Dumps the content of a Completion object to the log for debugging. + + Args: + i: The index of the completion. + comp: The Completion object to dump. + """ + max_logging.log(f"\n=== Completion {i} ===") + max_logging.log(f"index: {comp.index}") + max_logging.log(f"text: {repr(comp.text)}") + + + if comp.logprobs is None: + max_logging.log("logprobs: None") + return + + lp = comp.logprobs + # lengths should match: one logprob/offset per token + if not (len(lp.tokens) == len(lp.token_logprobs) == len(lp.text_offset)): + max_logging.log(f"[warn] mismatched lengths: tokens={len(lp.tokens)}, " + f"logps={len(lp.token_logprobs)}, offsets={len(lp.text_offset)}") + + max_logging.log("logprobs:") + max_logging.log(f" tokens (ids): {lp.tokens}") + max_logging.log(f" token_logprobs: {[round(x, 6) for x in lp.token_logprobs]}") + max_logging.log(f" text_offset: {lp.text_offset}") + max_logging.log(f" top_logprobs: {lp.top_logprobs}") + + max_logging.log(" tokens (decoded, id, logprob, offset):") + for tid, logp, off in zip(lp.tokens, lp.token_logprobs, lp.text_offset): + piece = llm.tokenizer.decode([int(tid)]) + max_logging.log(f" {repr(piece):>12s} id={int(tid):>6d} logp={logp:>10.6f} offset={off}") + + # When running standalone, basic logging is automatically configured. + # For server use, the server configures logging. + logging.basicConfig(level=logging.INFO) + + # Instantiate first to initialize JAX + llm = MaxTextGenerator(sys.argv) + + prompts_to_run = [ + "The capital of France is ", + ] + + max_tokens = 32 + echo = True + want_logprobs = 5 + temperature = 0.6 + seed = 72 + top_p = 0.95 + top_k = 20 + + max_logging.log( + f"\n--- Starting Batch Generation for {len(prompts_to_run)} Prompts " + f"(max_tokens={max_tokens}, echo={echo}) ---" + ) + + completions = llm.generate_batch( + prompts=prompts_to_run, + image_paths=None, + max_tokens=max_tokens, + logprobs=want_logprobs, + echo=echo, + seed=seed, + temperature=temperature, + top_p=top_p, + top_k=top_k + ) + + for i, comp in enumerate(completions): + dump_completion(i, comp) + + start = time.time() + + completions = llm.generate_batch( + prompts=prompts_to_run, + image_paths=None, + max_tokens=max_tokens, + logprobs=want_logprobs, + echo=echo, + seed=seed, + temperature=temperature, + top_p=top_p, + top_k=top_k + ) + + max_logging.log("--- Batch Generation Complete ---") + + for i, comp in enumerate(completions): + dump_completion(i, comp) + + end = time.time() + max_logging.log(f"total time: {end - start}") diff --git a/benchmarks/api_server/maxtext_server.py b/benchmarks/api_server/maxtext_server.py new file mode 100644 index 0000000000..46707bf1db --- /dev/null +++ b/benchmarks/api_server/maxtext_server.py @@ -0,0 +1,431 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import sys +import time +import uuid +import json +import signal +import asyncio +import threading +import queue +import logging +from typing import Union + +import uvicorn +from fastapi import FastAPI, HTTPException +import jax +import jax.numpy as jnp +from jax.experimental import multihost_utils + + +from benchmarks.api_server.maxtext_generator import MaxTextGenerator +from benchmarks.api_server.server_models import ( + CompletionRequest, + CompletionResponse, + CompletionChoice, + Usage, + ChatCompletionRequest, + ChatCompletionResponse, + ChatCompletionChoice, + ChatMessage, +) +from benchmarks.api_server import server_utils +from openai_harmony import ( + load_harmony_encoding, + HarmonyEncodingName, + Role, +) + +# ---------------------------- +# Init +# ---------------------------- + +# JAX distributed initialization must happen before any other JAX calls. +# We suppress the normal logger until after JAX is initialized. +logging.basicConfig(level=logging.WARNING) +print("Initializing MaxTextGenerator and JAX distributed system...") +llm = MaxTextGenerator(sys.argv) +rank = jax.process_index() + +# Now that JAX is initialized, we can get our rank-specific logger. +# The actual handler/formatter configuration will be done by Uvicorn. +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) # Ensure our logger passes INFO messages. +logger.info("MaxTextGenerator initialization complete.") + +harmony_enc = None +try: + harmony_enc = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS) + logger.info("Harmony encoding for gpt-oss loaded successfully.") +except ImportError: + logger.warning("openai_harmony not installed. GPT-OSS Harmony format will not be available.") +except Exception as e: + logger.error(f"Failed to load Harmony encoding: {e}") + + +app = FastAPI() + +# Global state for communication between threads. +request_queue = queue.Queue() +# A thread-safe dict to hold responses, keyed by request_id. +response_dict = {} +response_lock = threading.Lock() + +# Batching configuration +BATCH_TIMEOUT_S = 0.1 # 100ms +# Timeout for a client waiting for a response. +REQUEST_TIMEOUT_S = int(os.environ.get("MAXTEXT_REQUEST_TIMEOUT_S", "36000")) + + +async def _queue_and_wait_for_response(request: Union[CompletionRequest, ChatCompletionRequest]): + """ + Puts a request on the processing queue and waits for a response. + + This asynchronous function is the core of handling client requests. It generates + a unique ID for the request, places it in a global queue to be processed by + the batching loop, and then waits until the response is available in a + shared dictionary or until a timeout occurs. + + Args: + request: The incoming request object, either for a completion or a chat completion. + + Returns: + The response data once it's available. + + Raises: + HTTPException: If the request times out (504) or if an error occurs + during processing (500). + """ + request_id = f"req_{uuid.uuid4().hex}" + request_queue.put((request_id, request)) + + start_time = time.time() + while time.time() - start_time < REQUEST_TIMEOUT_S: + with response_lock: + if request_id in response_dict: + response_data = response_dict.pop(request_id) + if "error" in response_data: + raise HTTPException(status_code=500, detail=response_data["error"]) + return response_data + # Yield control to the event loop to allow other tasks to run. + await asyncio.sleep(0.05) + + raise HTTPException(status_code=504, detail="Request timed out.") + + +@app.post("/v1/completions", response_model=CompletionResponse) +async def create_completion(request: CompletionRequest): + """Handles completion requests with dynamic batching.""" + return await _queue_and_wait_for_response(request) + + +@app.post("/v1/chat/completions", response_model=ChatCompletionResponse) +async def create_chat_completion(request: ChatCompletionRequest): + """Handles chat completion requests with dynamic batching.""" + return await _queue_and_wait_for_response(request) + + +@app.get("/") +def health_check(): + """ + Provides a simple health check endpoint. + + Returns: + A dictionary indicating the server status. + """ + return {"status": "ok", "message": "MaxText API server is running."} + + +def run_server(): + """Runs the Uvicorn server in a separate thread.""" + uvicorn.run(app, host="0.0.0.0", port=8000) + + +# Define a maximum size for the request payload to be broadcasted. +# This avoids broadcasting variable-sized arrays, which can be complex. +MAX_REQUEST_SIZE = 65536 * 10 + + +def _build_chat_completion_response(request, completion_result, llm): + """Builds a ChatCompletionResponse from a single completion result.""" + text_out = completion_result.text + if "gpt-oss" in request.model and harmony_enc: + try: + parsed_messages = harmony_enc.parse_messages_from_completion_tokens( + completion_result.tokens, role=Role.ASSISTANT + ) + user_visible = "".join( + part.text for m in parsed_messages + if m.channel == "final" + for part in m.content + ) + if user_visible: + text_out = user_visible + else: + logger.warning("Harmony parsing for gpt-oss did not yield content in the 'final' channel. Falling back to raw text.") + except Exception as e: + logger.error(f"Harmony parsing failed for gpt-oss: {e}. Falling back to raw text.") + + want_top_logprobs = (request.top_logprobs or 0) > 0 if isinstance(request, ChatCompletionRequest) else (request.logprobs or 0) > 0 + lp_payload = server_utils.to_openai_logprobs( + getattr(completion_result, "logprobs", None), llm, want_top=want_top_logprobs + ) + text_out, lp_payload, finish_reason = server_utils.apply_stops_to_text_and_logprobs( + text_out, lp_payload, request.stop + ) + if finish_reason is None: + finish_reason = completion_result.finish_reason + + usage = Usage( + prompt_tokens=completion_result.prompt_token_count, + completion_tokens=completion_result.completion_token_count, + total_tokens=completion_result.prompt_token_count + completion_result.completion_token_count, + ) + return ChatCompletionResponse( + model=request.model, + choices=[ + ChatCompletionChoice( + index=0, + message=ChatMessage(role="assistant", content=text_out), + finish_reason=finish_reason, + logprobs=lp_payload, + ) + ], + usage=usage, + ) + + +def _build_completion_response(request, completions, prompts, llm): + """Builds a CompletionResponse from a list of completion results.""" + choices = [] + prompt_tokens_total = 0 + completion_tokens_total = 0 + + for idx, _ in enumerate(prompts): + item = completions[idx] + text_out = item.text + lp_payload = server_utils.to_openai_logprobs( + getattr(item, "logprobs", None), llm, want_top=(request.logprobs or 0) > 0 + ) + finish_reason = getattr(item, "finish_reason", "stop") + text_out, lp_payload, stop_reason = server_utils.apply_stops_to_text_and_logprobs( + text_out, lp_payload, request.stop + ) + if stop_reason is not None: + finish_reason = stop_reason + + prompt_tokens_total += item.prompt_token_count + completion_tokens_total += item.completion_token_count + + choices.append(CompletionChoice( + text=text_out, + index=idx, + logprobs=lp_payload, + finish_reason=finish_reason, + )) + + usage = Usage( + prompt_tokens=prompt_tokens_total, + completion_tokens=completion_tokens_total, + total_tokens=prompt_tokens_total + completion_tokens_total, + ) + return CompletionResponse( + model=request.model, + choices=choices, + usage=usage, + ) + + +def _create_response(request, completions, prompts, is_chat, llm): + """Creates either a CompletionResponse or ChatCompletionResponse.""" + if is_chat: + # Chat API only ever processes one prompt at a time from the server's perspective. + return _build_chat_completion_response(request, completions[0], llm) + else: + return _build_completion_response(request, completions, prompts, llm) + + +def _collect_batched_requests(): + """Waits for and collects a batch of requests from the queue.""" + batched_items = [] + start_time = time.time() + while len(batched_items) < llm.batch_size and (time.time() - start_time) < BATCH_TIMEOUT_S: + try: + item = request_queue.get(timeout=0.01) + batched_items.append(item) + except queue.Empty: + if batched_items: + break # Process what we have if timeout is reached + return batched_items + + +def _prepare_batch_for_broadcast(batched_items): + """Prepares the batch payload and request map for broadcasting.""" + first_request_id, first_request = batched_items[0] + + logprobs_param = None + if isinstance(first_request, ChatCompletionRequest): + if first_request.logprobs: + logprobs_param = first_request.top_logprobs if first_request.top_logprobs is not None else 1 + else: # CompletionRequest + logprobs_param = first_request.logprobs + + params = { + "max_tokens": first_request.max_tokens, "logprobs": logprobs_param, + "echo": getattr(first_request, "echo", False), "stop": first_request.stop, + "temperature": first_request.temperature, "seed": first_request.seed, + "top_k": first_request.top_k, "top_p": first_request.top_p, + } + + all_prompts = [] + request_info_map = [] + for req_id, req in batched_items: + is_chat = isinstance(req, ChatCompletionRequest) + prompts_for_req = server_utils.get_prompts_for_request(req, llm) + all_prompts.extend(prompts_for_req) + request_info_map.append((req_id, req, is_chat, len(prompts_for_req))) + + broadcast_payload = {"prompts": all_prompts, "params": params} + payload_bytes = json.dumps(broadcast_payload).encode("utf-8") + payload_len = len(payload_bytes) + + if payload_len > MAX_REQUEST_SIZE: + logger.error(f"Batched request is too large ({payload_len} bytes > {MAX_REQUEST_SIZE})") + for req_id, _, _, _ in request_info_map: + with response_lock: + response_dict[req_id] = {"error": "Batched request payload is too large."} + return 0, b'', [] # Signal other ranks to skip + + return payload_len, payload_bytes, request_info_map + + +def _process_results(completions, request_info_map, payload): + """Processes completions and sends responses back to the waiting threads.""" + logger.info(f"Batched generation finished. Processing {len(completions)} completions.") + completion_idx = 0 + for req_id, req, is_chat, num_prompts in request_info_map: + completions_for_req = completions[completion_idx : completion_idx + num_prompts] + prompts_for_req = payload["prompts"][completion_idx : completion_idx + num_prompts] + completion_idx += num_prompts + + response = _create_response(req, completions_for_req, prompts_for_req, is_chat, llm) + with response_lock: + response_dict[req_id] = response + + +def main_loop(): + """The main processing loop with dynamic batching for all JAX processes.""" + while True: + payload_len, payload_bytes, request_info_map = 0, b'', [] + if jax.process_index() == 0: + batched_items = _collect_batched_requests() + if batched_items: + payload_len, payload_bytes, request_info_map = _prepare_batch_for_broadcast(batched_items) + + # Broadcast the payload to all ranks + data_to_broadcast = ( + jnp.array([payload_len], dtype=jnp.int32), + jnp.pad(jnp.frombuffer(payload_bytes, dtype=jnp.uint8), (0, MAX_REQUEST_SIZE - payload_len)), + ) + received_len_array, received_data_array = multihost_utils.broadcast_one_to_all(data_to_broadcast) + + received_len = int(received_len_array[0]) + if received_len == 0: + time.sleep(0.01) + continue + + broadcasted_data = received_data_array[:received_len].tobytes().decode("utf-8") + payload = json.loads(broadcasted_data) + + try: + if jax.process_index() == 0: + logger.info(f"Starting batched generation for {len(payload['prompts'])} prompts with params: {payload['params']}") + + completions = llm.generate_batch(prompts=payload["prompts"], **payload["params"]) + + if jax.process_index() == 0: + _process_results(completions, request_info_map, payload) + + except Exception as e: + logger.error(f"Inference failed for batch: {e}", exc_info=True) + if jax.process_index() == 0: + for req_id, _, _, _ in request_info_map: + with response_lock: + response_dict[req_id] = {"error": f"Inference failed: {e}"} + + +if __name__ == "__main__": + server_thread = None + server = None + + # The coordinator process (rank 0) runs the FastAPI server in a separate thread. + if jax.process_index() == 0: + # Define a Uvicorn-compatible logging config. + LOGGING_CONFIG = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "default": { + "()": "uvicorn.logging.DefaultFormatter", + "fmt": f"%(levelprefix)s RANK {rank}: %(message)s", + "use_colors": None, + }, + "access": { + "()": "uvicorn.logging.AccessFormatter", + "fmt": f'%(levelprefix)s RANK {rank}: %(client_addr)s - "%(request_line)s" %(status_code)s', + }, + }, + "handlers": { + "default": { + "formatter": "default", + "class": "logging.StreamHandler", + "stream": "ext://sys.stderr", + }, + "access": { + "formatter": "access", + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", + }, + }, + "loggers": { + "": {"handlers": ["default"], "level": "INFO"}, + "uvicorn.error": {"level": "INFO"}, + "uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False}, + }, + } + + config = uvicorn.Config(app, host="0.0.0.0", port=8000, log_config=LOGGING_CONFIG) + server = uvicorn.Server(config) + server_thread = threading.Thread(target=server.run) + + logger.info(f"Starting Uvicorn server in a background thread on coordinator process {jax.process_index()}...") + server_thread.start() + + try: + # All processes (coordinator and workers) enter the main processing loop. + logger.info(f"Process {jax.process_index()} is entering the main processing loop.") + main_loop() + except KeyboardInterrupt: + logger.info(f"Process {jax.process_index()} received KeyboardInterrupt. Shutting down.") + finally: + if jax.process_index() == 0 and server is not None and server_thread is not None: + logger.info("Stopping Uvicorn server...") + server.should_exit = True + server_thread.join() + logger.info("Uvicorn server stopped.") + + logger.info(f"Process {jax.process_index()} has exited.") diff --git a/benchmarks/api_server/port_forward_xpk.sh b/benchmarks/api_server/port_forward_xpk.sh new file mode 100644 index 0000000000..625440d5ed --- /dev/null +++ b/benchmarks/api_server/port_forward_xpk.sh @@ -0,0 +1,104 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +#!/bin/bash +# +# This script automates finding the correct pod in a MaxText server workload +# and establishes a port-forward connection to it. +# +# Usage: +# bash port_forward_xpk.sh job_name= project= zone= cluster= [namespace=] + +set -eu # Exit immediately if a command exits with a non-zero status or if an unset variable is used. + +# --- Argument Parsing --- +NAMESPACE="default" # Default namespace + +for arg in "$@" +do + case $arg in + job_name=*) + JOB_NAME="${arg#*=}" + # Shift removes the current argument from the list of positional parameters ($@). + shift + ;; + project=*) + PROJECT="${arg#*=}" + shift + ;; + zone=*) + ZONE="${arg#*=}" + shift + ;; + cluster=*) + CLUSTER="${arg#*=}" + shift + ;; + namespace=*) + NAMESPACE="${arg#*=}" + shift + ;; + esac +done + +# --- Validate Arguments --- +if [ -z "$JOB_NAME" ] || [ -z "$PROJECT" ] || [ -z "$ZONE" ] || [ -z "$CLUSTER" ]; then + echo "Usage: $0 job_name= project= zone= cluster= [namespace=]" >&2 + exit 1 +fi + +echo "--- Configuration ---" +echo "Project: $PROJECT" +echo "Zone: $ZONE" +echo "Cluster: $CLUSTER" +echo "Job Name: $JOB_NAME" +echo "Namespace: $NAMESPACE" +echo "---------------------" + +# --- Get GKE Credentials --- +echo "Fetching cluster credentials..." +gcloud container clusters get-credentials "$CLUSTER" --zone "$ZONE" --project "$PROJECT" > /dev/null + +# --- Find the Server Pod --- +echo "Searching for pods in namespace '$NAMESPACE' with label 'job-name=$JOB_NAME'..." +# Use a label selector for an efficient server-side lookup. +# Read the space-separated pod names safely into a bash array. +read -r -a PODS <<< "$(kubectl get pods -n "$NAMESPACE" -l "job-name=$JOB_NAME" -o jsonpath='{.items[*].metadata.name}')" + +if [ -z "$PODS" ]; then + echo "Error: No pods found for job name '$JOB_NAME' in namespace '$NAMESPACE'." + exit 1 +fi + +SERVER_POD="" +for pod in "${PODS[@]}"; do + echo "Checking logs for pod: $pod..." + # Use grep -q for a silent check. The command succeeds if the pattern is found. + if kubectl logs "$pod" -n "$NAMESPACE" | grep -q "Uvicorn running on http://0.0.0.0:8000"; then + echo "Found server running in pod: $pod" + SERVER_POD=$pod + break # Exit the loop once the server pod is found + fi +done + +# --- Establish Port Forwarding --- +if [ -n "$SERVER_POD" ]; then + echo "Establishing port-forward from localhost:8000 to $SERVER_POD:8000 in namespace '$NAMESPACE'..." + echo "You can now send requests to http://localhost:8000" + kubectl port-forward "pod/$SERVER_POD" -n "$NAMESPACE" 8000:8000 +else + echo "Error: Could not find a pod running the Uvicorn server for job '$JOB_NAME' in namespace '$NAMESPACE'." + exit 1 +fi diff --git a/benchmarks/api_server/requirements.txt b/benchmarks/api_server/requirements.txt new file mode 100644 index 0000000000..ac5d4046b4 --- /dev/null +++ b/benchmarks/api_server/requirements.txt @@ -0,0 +1,4 @@ +uvicorn +fastapi +openai-harmony +pyyaml \ No newline at end of file diff --git a/benchmarks/api_server/server_models.py b/benchmarks/api_server/server_models.py new file mode 100644 index 0000000000..cff25db031 --- /dev/null +++ b/benchmarks/api_server/server_models.py @@ -0,0 +1,206 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import time +import uuid +from typing import List, Optional, Union, Dict, TypeVar, Generic + +from pydantic import BaseModel, Field, field_validator + + +class SamplingParams(BaseModel): + """ + Defines the common sampling parameters that are shared across different types of + generation requests, such as standard completions and chat-based completions. + + Attributes: + max_tokens: The maximum number of tokens to generate. + temperature: The sampling temperature. + top_p: The nucleus sampling probability. + top_k: The top-k sampling integer. + stream: Whether to stream the response. + stop: A string or list of strings that will stop the generation. + seed: A seed for deterministic sampling. + """ + max_tokens: Optional[int] = None + temperature: Optional[float] = None + top_p: Optional[float] = None + top_k: Optional[int] = None + stream: Optional[bool] = False + stop: Optional[Union[str, List[str]]] = None + seed: Optional[int] = None + + +class CompletionRequest(SamplingParams): + """ + Represents a request for a standard text completion, inheriting sampling + parameters from `SamplingParams`. + + Attributes: + model: The ID of the model to use for the completion. + prompt: The prompt(s) to generate completions for, which can be a string, + a list of strings, a list of token IDs, or a list of lists of token IDs. + echo: Whether to echo the prompt back in the response. + logprobs: The number of top log probabilities to return for each token. + """ + model: str + prompt: Union[str, List[str], List[int], List[List[int]]] + echo: Optional[bool] = False + logprobs: Optional[int] = None + + @field_validator("logprobs") + def validate_logprobs(cls, v): + if v is not None and v < 0: + raise ValueError("logprobs must be a non-negative integer if provided.") + return v + + +class LogProbsPayload(BaseModel): + """ + A data structure to hold the log probability information for a sequence of tokens, + formatted to be compatible with OpenAI's API. + + Attributes: + tokens: The string representation of each token. + token_logprobs: The log probability of each token. + top_logprobs: A list of dictionaries mapping other tokens to their log + probabilities at each position. + text_offset: The character offset of each token in the text. + """ + tokens: List[str] + token_logprobs: List[Optional[float]] + top_logprobs: Optional[List[Optional[Dict[str, float]]]] = None + text_offset: List[int] + + +class CompletionChoice(BaseModel): + """ + Represents a single choice (a possible completion) in a `CompletionResponse`. + + Attributes: + text: The generated text for this choice. + index: The index of this choice in the list of choices. + logprobs: An optional payload containing log probability information. + finish_reason: The reason the model stopped generating tokens (e.g., 'stop', 'length'). + """ + text: str + index: int + logprobs: Optional[LogProbsPayload] = None + finish_reason: str = "stop" + + +class Usage(BaseModel): + """ + Provides information about the number of tokens used in a request. + + Attributes: + prompt_tokens: The number of tokens in the input prompt. + completion_tokens: The number of tokens in the generated completion. + total_tokens: The total number of tokens used. + """ + prompt_tokens: int + completion_tokens: int + total_tokens: int + + +# Define a TypeVar for the choice models +ChoiceType = TypeVar('ChoiceType') + + +class BaseCompletionResponse(BaseModel, Generic[ChoiceType]): + """ + A generic base response model using Python's Generic type. It shares all + common fields for API responses and uses a TypeVar for the 'choices' list + to accommodate different types of choices (e.g., for standard vs. chat completions). + + Attributes: + id: A unique identifier for the response. + object: The type of the object (e.g., 'text_completion'). + created: The timestamp when the response was created. + model: The model that generated the response. + choices: A list of choices, the type of which is determined by `ChoiceType`. + usage: Token usage statistics. + """ + id: str + object: str + created: int = Field(default_factory=lambda: int(time.time())) + model: str + choices: List[ChoiceType] + usage: Usage + + +class CompletionResponse(BaseCompletionResponse[CompletionChoice]): + """ + The specific response object for a standard completion request. It inherits + from the generic base and specifies `CompletionChoice` as its choice type. + It also provides default values for the 'id' and 'object' fields. + """ + id: str = Field(default_factory=lambda: f"cmpl-{uuid.uuid4().hex}") + object: str = "text_completion" + + +class ChatMessage(BaseModel): + """ + Represents a single message within a chat conversation. + + Attributes: + role: The role of the message's author (e.g., 'user', 'assistant'). + content: The text content of the message. + """ + role: str + content: str + + +class ChatCompletionRequest(SamplingParams): + """ + Represents a request for a chat-based completion, where the input is a + sequence of messages. Inherits sampling parameters from `SamplingParams`. + + Attributes: + model: The ID of the model to use. + messages: A list of `ChatMessage` objects representing the conversation history. + logprobs: Whether to return log probabilities. + top_logprobs: The number of top log probabilities to return if `logprobs` is true. + """ + model: str + messages: List[ChatMessage] + logprobs: Optional[bool] = False + top_logprobs: Optional[int] = None + + +class ChatCompletionChoice(BaseModel): + """ + Represents a single choice in a `ChatCompletionResponse`. + + Attributes: + index: The index of this choice. + message: The `ChatMessage` generated by the model. + finish_reason: The reason the model stopped generating. + logprobs: An optional payload with log probability information. + """ + index: int + message: ChatMessage + finish_reason: str = "stop" + logprobs: Optional[LogProbsPayload] = None + + +class ChatCompletionResponse(BaseCompletionResponse[ChatCompletionChoice]): + """ + The specific response object for a chat completion request. It inherits from + the generic base, specifies `ChatCompletionChoice` as its choice type, and + provides chat-specific default values for 'id' and 'object'. + """ + id: str = Field(default_factory=lambda: f"chatcmpl-{uuid.uuid4().hex}") + object: str = "chat.completion" diff --git a/benchmarks/api_server/server_utils.py b/benchmarks/api_server/server_utils.py new file mode 100644 index 0000000000..04849559a4 --- /dev/null +++ b/benchmarks/api_server/server_utils.py @@ -0,0 +1,300 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os +import bisect +import math +import yaml +import json +import logging +from datetime import datetime, timezone +from typing import List, Optional, Union, Dict, Any + +from fastapi import HTTPException + +from benchmarks.api_server.maxtext_generator import MaxTextGenerator +from benchmarks.api_server.server_models import LogProbsPayload + +# ---------------------------- +# Debugging +# ---------------------------- + +DEBUG_MODE = os.environ.get("MAXTEXT_SERVER_DEBUG", "0") == "1" +DEBUG_LOG_FILE = os.environ.get("MAXTEXT_DEBUG_LOG_FILE", "benchmarks/api_server/server_debug_log.jsonl") +logger = logging.getLogger(__name__) + + +def log_debug_event(request_id: str, event_type: str, content: dict): + """ + Helper to write a structured debug log entry if DEBUG_MODE is on. + + Args: + request_id: The unique identifier for the request. + event_type: A string describing the type of event being logged (e.g., 'request', 'response'). + content: A dictionary containing the data to be logged. + """ + if not DEBUG_MODE: + return + try: + log_entry = { + "timestamp": datetime.now(timezone.utc).isoformat(), + "request_id": request_id, + "event": event_type, + "content": content, + } + with open(DEBUG_LOG_FILE, "a") as f: + f.write(json.dumps(log_entry) + "\n") + except Exception as e: + # Use logger for errors + logger.error(f"Error writing to debug log file '{DEBUG_LOG_FILE}': {e}") + + +# ---------------------------- +# Request/Response Helpers +# ---------------------------- + + +def decode_one_prompt(p: Union[str, List[int]], llm: MaxTextGenerator) -> str: + """ + Decodes a single prompt element, which can be a string or a list of token IDs. + + Args: + p: The prompt element to decode. + llm: The MaxTextGenerator instance, used for its tokenizer. + + Returns: + The decoded prompt as a string. + + Raises: + ValueError: If the prompt item has an unsupported type. + """ + if isinstance(p, str): + return p + if isinstance(p, list) and (len(p) == 0 or isinstance(p[0], int)): + try: + return llm.tokenizer.decode(p) + except Exception: + print("Return empty string on decoding error") + return "" + raise ValueError("Unsupported prompt item type") + + +def get_prompts_for_request(req: any, llm: MaxTextGenerator) -> List[str]: + """ + Extracts and formats a list of prompts from a request object. + + This function handles both standard `CompletionRequest` and `ChatCompletionRequest` + types, converting them into a unified list of string prompts that the model + can process. + + Args: + req: The request object. + llm: The MaxTextGenerator instance. + + Returns: + A list of string prompts. + """ + if hasattr(req, 'messages'): # ChatCompletionRequest + messages = [m.model_dump() for m in req.messages] + formatted_prompt = llm.tokenizer.tokenizer.apply_chat_template( + messages, tokenize=False, add_generation_prompt=True + ) + return [formatted_prompt] + else: # CompletionRequest + return normalize_prompts(req.prompt, llm) + + +def normalize_prompts(prompt: Union[str, List[str], List[int], List[List[int]]], llm: MaxTextGenerator) -> List[str]: + """ + Normalizes the highly flexible 'prompt' field from an OpenAI-style request + into a simple list of strings. + + The 'prompt' field can be a single string, a list of strings, a list of + token IDs, or a list of lists of token IDs. This function handles all + these cases and returns a flat list of string prompts. + + Args: + prompt: The prompt data from the request. + llm: The MaxTextGenerator instance for decoding token IDs. + + Returns: + A list of normalized string prompts. + + Raises: + HTTPException: If the prompt type is not supported. + """ + if isinstance(prompt, str): + return [prompt] + if isinstance(prompt, list): + if len(prompt) == 0: + return [] + # Prompts can be a list of strings, a single list of ints, or a list of lists of ints. + first = prompt[0] + if isinstance(first, str): + return [str(x) for x in prompt] + if isinstance(first, int): + return [decode_one_prompt(prompt, llm)] + if isinstance(first, list): + return [decode_one_prompt(x, llm) for x in prompt] + raise HTTPException(status_code=400, detail="Unsupported prompt type for this API.") + + +def decode_token_id(token_id: int, llm: MaxTextGenerator) -> str: + """ + Decodes a single token ID into its string representation. + + Args: + token_id: The integer token ID to decode. + llm: The MaxTextGenerator instance. + + Returns: + The decoded string. + """ + return llm.tokenizer.decode([int(token_id)]) + + +def finite_or_none(v: Optional[float]) -> Optional[float]: + """ + Returns the float if it's finite (i.e., not NaN or infinity), otherwise None. + + Args: + v: The float value to check. + + Returns: + The original float if it is finite, otherwise None. + """ + if v is None: + return None + f = float(v) + return f if math.isfinite(f) else None + + +def to_openai_logprobs(lp_obj: Any, llm: MaxTextGenerator, want_top: bool = True) -> Optional[LogProbsPayload]: + """ + Converts the internal logprobs object to the OpenAI-compatible format. + + Args: + lp_obj: The internal logprobs object from the generation result. + llm: The MaxTextGenerator instance for decoding tokens. + want_top: Whether to populate the `top_logprobs` field. + + Returns: + A `LogProbsPayload` object compatible with the OpenAI API, or None. + """ + if lp_obj is None: + return None + + token_strings = [decode_token_id(tid, llm) for tid in lp_obj.tokens] + token_logprobs = [finite_or_none(v) for v in lp_obj.token_logprobs] + text_offset = list(lp_obj.text_offset) + + # Ensure all lists are of the same length to avoid errors. + min_len = min(len(token_strings), len(token_logprobs), len(text_offset)) + token_strings = token_strings[:min_len] + token_logprobs = token_logprobs[:min_len] + text_offset = text_offset[:min_len] + + top_logprobs: Optional[List[Optional[Dict[str, float]]]] = None + if want_top: + # The current implementation only returns the logprob of the single sampled token. + # This structure is a placeholder for a future feature where the model might + # return the logprobs of multiple top tokens at each step. + top_logprobs = [ + ({tok: lp} if lp is not None else None) + for tok, lp in zip(token_strings, token_logprobs) + ] + + return LogProbsPayload( + tokens=token_strings, + token_logprobs=token_logprobs, + top_logprobs=top_logprobs, + text_offset=text_offset, + ) + + +def count_tokens(s: str, llm: MaxTextGenerator) -> int: + """ + Counts the number of tokens in a string. + + Args: + s: The string to tokenize. + llm: The MaxTextGenerator instance. + + Returns: + The number of tokens in the string. + """ + try: + # Use the underlying tokenizer to avoid the jetstream wrapper's + # padding issues with single-token sequences. + ids = llm.tokenizer.tokenizer.encode(s, add_special_tokens=False) + return len(ids) + except Exception as e: + logger.warning(f"Could not count tokens for string '{s[:50]}...': {e}") + return 0 + + +def apply_stops_to_text_and_logprobs( + text: str, + logprobs_payload: Optional[LogProbsPayload], + stop: Optional[Union[str, List[str]]], +) -> tuple[str, Optional[LogProbsPayload], Optional[str]]: + """ + Truncates the generated text and corresponding logprobs at the first occurrence + of any of the specified stop sequences. + + Args: + text: The generated text. + logprobs_payload: The corresponding logprobs payload. + stop: The stop sequence(s) to search for. + + Returns: + A tuple containing the truncated text, the truncated logprobs payload, + and the reason for stopping ('stop' if a sequence was found, otherwise None). + """ + if not stop: + return text, logprobs_payload, None + + stops = [stop] if isinstance(stop, str) else list(stop) + + # Find the earliest stop sequence + first_stop_index = -1 + for s in stops: + if not s: + continue + i = text.find(s) + if i != -1: + first_stop_index = i if first_stop_index == -1 else min(first_stop_index, i) + + if first_stop_index == -1: + return text, logprobs_payload, None + + # Truncate text + new_text = text[:first_stop_index] + + # Truncate logprobs payload if it exists + if logprobs_payload is not None: + truncate_at_index = bisect.bisect_left( + logprobs_payload.text_offset, first_stop_index + ) + + new_logprobs = LogProbsPayload( + tokens=logprobs_payload.tokens[:truncate_at_index], + token_logprobs=logprobs_payload.token_logprobs[:truncate_at_index], + top_logprobs=logprobs_payload.top_logprobs[:truncate_at_index] if logprobs_payload.top_logprobs is not None else None, + text_offset=logprobs_payload.text_offset[:truncate_at_index], + ) + return new_text, new_logprobs, "stop" + + return new_text, logprobs_payload, "stop" diff --git a/benchmarks/api_server/start_server.sh b/benchmarks/api_server/start_server.sh new file mode 100644 index 0000000000..7d9b9aa431 --- /dev/null +++ b/benchmarks/api_server/start_server.sh @@ -0,0 +1,60 @@ +# Copyright 2023–2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +#!/bin/bash +# This script starts the MaxText server from the project root, +# ensuring that Python can find the necessary modules regardless of +# where the script is invoked from. +# +# Example: +# bash benchmarks/api_server/start_server.sh \ +# MaxText/configs/base.yml \ +# model_name="qwen3-30b-a3b" \ +# tokenizer_path="Qwen/Qwen3-30B-A3B-Thinking-2507" \ +# load_parameters_path="" \ +# per_device_batch_size=4 \ +# ici_tensor_parallelism=4 \ +# max_prefill_predict_length=1024 \ +# max_target_length=2048 \ +# async_checkpointing=false \ +# scan_layers=false \ +# attention="dot_product" \ +# tokenizer_type="huggingface" \ +# return_log_prob=True +set -e + + +# Check if arguments were provided. +if [ -z "$1" ]; then + echo "Usage: $0 [arg1=value1 arg2=value2 ...]" >&2 + echo "Or: $0 " >&2 + exit 1 +fi + +# Get the absolute path of the directory where the script is located. +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +# The project root is two levels up from the script's directory. +PROJECT_ROOT=$(dirname $(dirname "$SCRIPT_DIR")) + +# Change to the project root directory. +cd "$PROJECT_ROOT" + +echo "Starting MaxText server on http://0.0.0.0:8000" +echo "Executing from project root: $(pwd)" +echo "Using arguments: $@" + +# Pass all script arguments directly to the python module. +python -u -m benchmarks.api_server.maxtext_server "$@"