From 3c0b41b30559efcb72609bc451caf64bab94d912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 21 Oct 2025 11:23:16 +0200 Subject: [PATCH 1/5] move doc to Swift Docc and keep README minimal --- .gitignore | 1 + .spi.yml | 4 + Package.swift | 1 + README.md | 1224 +---------------- .../BedrockAuthentication+JWT.swift | 0 .../BedrockAuthentication.swift | 0 .../{ => BedrockService}/BedrockModel.swift | 1 + .../{ => BedrockService}/BedrockService.swift | 0 .../BedrockServiceError.swift | 0 .../Converse/BedrockService+Converse.swift | 4 + .../BedrockService+ConverseStreaming.swift | 4 + .../Converse/ContentBlocks/Content.swift | 0 .../ContentBlocks/DocumentBlock.swift | 0 .../ContentBlocks/DocumentToJSON.swift | 0 .../Converse/ContentBlocks/ImageBlock.swift | 0 .../ContentBlocks/JSONtoDocument.swift | 0 .../ContentBlocks/ReasoningBlock.swift | 0 .../Converse/ContentBlocks/S3Location.swift | 0 .../ContentBlocks/ToolResultBlock.swift | 0 .../Converse/ContentBlocks/ToolUseBlock.swift | 0 .../Converse/ContentBlocks/VideoBlock.swift | 0 .../Converse/ConverseReply.swift | 0 .../Converse/ConverseRequest.swift | 0 .../Converse/ConverseRequestBuilder.swift | 0 .../Converse/ConverseRequestStreaming.swift | 0 .../Converse/ConverseResponseStreaming.swift | 0 .../Converse/History.swift | 0 .../{ => BedrockService}/Converse/JSON.swift | 0 .../Converse/Message.swift | 0 .../{ => BedrockService}/Converse/Role.swift | 0 .../Streaming/ConverseReplyStream.swift | 0 .../Streaming/ConverseStreamElement.swift | 0 .../Converse/Streaming/ResponseMetaData.swift | 0 .../Converse/Streaming/ToolUseStart.swift | 0 .../{ => BedrockService}/Converse/Tool.swift | 0 .../BedrockService/Docs.docc/AddingModels.md | 299 ++++ .../Docs.docc/Authentication.md | 96 ++ .../Docs.docc/BedrockService.md | 40 + Sources/BedrockService/Docs.docc/Converse.md | 92 ++ Sources/BedrockService/Docs.docc/Documents.md | 139 ++ .../BedrockService/Docs.docc/Embeddings.md | 274 ++++ .../Docs.docc/ImageGeneration.md | 190 +++ .../BedrockService/Docs.docc/QuickStart.md | 65 + Sources/BedrockService/Docs.docc/Reasoning.md | 186 +++ .../Docs.docc/Resources/bedrock.png | Bin 0 -> 191481 bytes Sources/BedrockService/Docs.docc/Streaming.md | 103 ++ .../Docs.docc/TextGeneration.md | 167 +++ Sources/BedrockService/Docs.docc/Tools.md | 215 +++ Sources/BedrockService/Docs.docc/Vision.md | 109 ++ ...rockService+ImageParameterValidation.swift | 12 +- ...BedrockService+InvokeModelEmbeddings.swift | 1 + .../BedrockService+InvokeModelImage.swift | 0 .../BedrockService+InvokeModelText.swift | 0 .../InvokeModel/ContentType.swift | 0 .../InvokeModel/Embeddings.swift | 0 .../InvokeModel/ImageGenerationOutput.swift | 0 .../InvokeModel/ImageResolution.swift | 0 .../InvokeModel/InvokeModelRequest.swift | 0 .../InvokeModel/InvokeModelResponse.swift | 0 .../InvokeModel/Protocols.swift | 0 .../InvokeModel/TextCompletion.swift | 0 .../ListModels/ModelSummary.swift | 0 .../Modalities/ConverseFeature.swift | 0 .../Modalities/ConverseModality.swift | 0 .../Modalities/CrossRegionInference.swift | 0 .../Modalities/EmbeddingsModality.swift | 0 .../Modalities/ImageModality.swift | 0 .../Modalities/Modality.swift | 0 .../Modalities/StandardConverse.swift | 0 .../Modalities/TextModality.swift | 0 .../Models/Amazon/AmazonImage.swift | 0 .../Amazon/AmazonImageRequestBody.swift | 23 +- .../Amazon/AmazonImageResponseBody.swift | 0 .../Models/Amazon/Nova/Nova.swift | 0 .../Amazon/Nova/NovaBedrockModels.swift | 0 .../Nova/NovaImageResolutionValidator.swift | 0 .../Models/Amazon/Nova/NovaRequestBody.swift | 0 .../Models/Amazon/Nova/NovaResponseBody.swift | 0 .../Models/Amazon/TaskType.swift | 0 .../Titan/TitanImageResolutionValidator.swift | 0 .../Amazon/Titan/TitanRequestBody.swift | 0 .../Amazon/Titan/TitanResponseBody.swift | 0 .../Models/Amazon/Titan/TitanText.swift | 0 .../Amazon/Titan/TitanTextBedrockModels.swift | 0 .../TitanEmbeddings/TitanEmbeddings.swift | 0 .../TitanEmbeddingsBedrockModels.swift | 0 .../TitanEmbeddingsRequestBody.swift | 0 .../TitanEmbeddingsResponseBody.swift | 0 .../Models/Anthropic/Anthropic.swift | 0 .../Anthropic/AnthropicBedrockModels.swift | 0 .../Anthropic/AnthropicRequestBody.swift | 0 .../Anthropic/AnthropicResponseBody.swift | 0 .../Models/Cohere/CohereBedrockModels.swift | 0 .../Models/DeepSeek/DeepSeek.swift | 0 .../DeepSeek/DeepSeekBedrockModels.swift | 0 .../Models/DeepSeek/DeepSeekRequestBody.swift | 0 .../DeepSeek/DeepSeekResponseBody.swift | 0 .../Models/Jamba/JambaBedrockModels.swift | 0 .../Models/Llama/Llama.swift | 0 .../Models/Llama/LlamaBedrockModels.swift | 0 .../Models/Llama/LlamaRequestBody.swift | 0 .../Models/Llama/LlamaResponseBody.swift | 0 .../Models/Mistral/MistralBedrockModels.swift | 0 .../Models/OpenAI/OpenAI.swift | 0 .../Models/OpenAI/OpenAIBedrockModels.swift | 0 .../Models/OpenAI/OpenAIRequestBody.swift | 0 .../Models/OpenAI/OpenAIResponseBody.swift | 0 .../Parameters/ConverseParameters.swift | 0 .../Parameters/EmbeddingsParameters.swift | 0 .../ImageGenerationParameters.swift | 0 .../Parameters/ParameterName.swift | 0 .../Parameters/Parameters.swift | 0 .../Parameters/TextGenerationParameters.swift | 0 .../Protocols/BedrockClientProtocol.swift | 0 .../Protocols/BedrockConfigProtocol.swift | 0 .../BedrockRuntimeClientProtocol.swift | 0 Sources/{ => BedrockService}/Region.swift | 0 117 files changed, 2055 insertions(+), 1195 deletions(-) create mode 100644 .spi.yml rename Sources/{ => BedrockService}/BedrockAuthentication+JWT.swift (100%) rename Sources/{ => BedrockService}/BedrockAuthentication.swift (100%) rename Sources/{ => BedrockService}/BedrockModel.swift (99%) rename Sources/{ => BedrockService}/BedrockService.swift (100%) rename Sources/{ => BedrockService}/BedrockServiceError.swift (100%) rename Sources/{ => BedrockService}/Converse/BedrockService+Converse.swift (96%) rename Sources/{ => BedrockService}/Converse/BedrockService+ConverseStreaming.swift (97%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/Content.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/DocumentBlock.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/DocumentToJSON.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/ImageBlock.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/JSONtoDocument.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/ReasoningBlock.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/S3Location.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/ToolResultBlock.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/ToolUseBlock.swift (100%) rename Sources/{ => BedrockService}/Converse/ContentBlocks/VideoBlock.swift (100%) rename Sources/{ => BedrockService}/Converse/ConverseReply.swift (100%) rename Sources/{ => BedrockService}/Converse/ConverseRequest.swift (100%) rename Sources/{ => BedrockService}/Converse/ConverseRequestBuilder.swift (100%) rename Sources/{ => BedrockService}/Converse/ConverseRequestStreaming.swift (100%) rename Sources/{ => BedrockService}/Converse/ConverseResponseStreaming.swift (100%) rename Sources/{ => BedrockService}/Converse/History.swift (100%) rename Sources/{ => BedrockService}/Converse/JSON.swift (100%) rename Sources/{ => BedrockService}/Converse/Message.swift (100%) rename Sources/{ => BedrockService}/Converse/Role.swift (100%) rename Sources/{ => BedrockService}/Converse/Streaming/ConverseReplyStream.swift (100%) rename Sources/{ => BedrockService}/Converse/Streaming/ConverseStreamElement.swift (100%) rename Sources/{ => BedrockService}/Converse/Streaming/ResponseMetaData.swift (100%) rename Sources/{ => BedrockService}/Converse/Streaming/ToolUseStart.swift (100%) rename Sources/{ => BedrockService}/Converse/Tool.swift (100%) create mode 100644 Sources/BedrockService/Docs.docc/AddingModels.md create mode 100644 Sources/BedrockService/Docs.docc/Authentication.md create mode 100644 Sources/BedrockService/Docs.docc/BedrockService.md create mode 100644 Sources/BedrockService/Docs.docc/Converse.md create mode 100644 Sources/BedrockService/Docs.docc/Documents.md create mode 100644 Sources/BedrockService/Docs.docc/Embeddings.md create mode 100644 Sources/BedrockService/Docs.docc/ImageGeneration.md create mode 100644 Sources/BedrockService/Docs.docc/QuickStart.md create mode 100644 Sources/BedrockService/Docs.docc/Reasoning.md create mode 100644 Sources/BedrockService/Docs.docc/Resources/bedrock.png create mode 100644 Sources/BedrockService/Docs.docc/Streaming.md create mode 100644 Sources/BedrockService/Docs.docc/TextGeneration.md create mode 100644 Sources/BedrockService/Docs.docc/Tools.md create mode 100644 Sources/BedrockService/Docs.docc/Vision.md rename Sources/{ => BedrockService}/InvokeModel/BedrockService+ImageParameterValidation.swift (91%) rename Sources/{ => BedrockService}/InvokeModel/BedrockService+InvokeModelEmbeddings.swift (98%) rename Sources/{ => BedrockService}/InvokeModel/BedrockService+InvokeModelImage.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/BedrockService+InvokeModelText.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/ContentType.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/Embeddings.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/ImageGenerationOutput.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/ImageResolution.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/InvokeModelRequest.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/InvokeModelResponse.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/Protocols.swift (100%) rename Sources/{ => BedrockService}/InvokeModel/TextCompletion.swift (100%) rename Sources/{ => BedrockService}/ListModels/ModelSummary.swift (100%) rename Sources/{ => BedrockService}/Modalities/ConverseFeature.swift (100%) rename Sources/{ => BedrockService}/Modalities/ConverseModality.swift (100%) rename Sources/{ => BedrockService}/Modalities/CrossRegionInference.swift (100%) rename Sources/{ => BedrockService}/Modalities/EmbeddingsModality.swift (100%) rename Sources/{ => BedrockService}/Modalities/ImageModality.swift (100%) rename Sources/{ => BedrockService}/Modalities/Modality.swift (100%) rename Sources/{ => BedrockService}/Modalities/StandardConverse.swift (100%) rename Sources/{ => BedrockService}/Modalities/TextModality.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/AmazonImage.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/AmazonImageRequestBody.swift (90%) rename Sources/{ => BedrockService}/Models/Amazon/AmazonImageResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Nova/Nova.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Nova/NovaBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Nova/NovaImageResolutionValidator.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Nova/NovaRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Nova/NovaResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/TaskType.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Titan/TitanImageResolutionValidator.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Titan/TitanRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Titan/TitanResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Titan/TitanText.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/Titan/TitanTextBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/TitanEmbeddings/TitanEmbeddings.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/TitanEmbeddings/TitanEmbeddingsBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/TitanEmbeddings/TitanEmbeddingsRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/Amazon/TitanEmbeddings/TitanEmbeddingsResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Anthropic/Anthropic.swift (100%) rename Sources/{ => BedrockService}/Models/Anthropic/AnthropicBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/Anthropic/AnthropicRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/Anthropic/AnthropicResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Cohere/CohereBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/DeepSeek/DeepSeek.swift (100%) rename Sources/{ => BedrockService}/Models/DeepSeek/DeepSeekBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/DeepSeek/DeepSeekRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/DeepSeek/DeepSeekResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Jamba/JambaBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/Llama/Llama.swift (100%) rename Sources/{ => BedrockService}/Models/Llama/LlamaBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/Llama/LlamaRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/Llama/LlamaResponseBody.swift (100%) rename Sources/{ => BedrockService}/Models/Mistral/MistralBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/OpenAI/OpenAI.swift (100%) rename Sources/{ => BedrockService}/Models/OpenAI/OpenAIBedrockModels.swift (100%) rename Sources/{ => BedrockService}/Models/OpenAI/OpenAIRequestBody.swift (100%) rename Sources/{ => BedrockService}/Models/OpenAI/OpenAIResponseBody.swift (100%) rename Sources/{ => BedrockService}/Parameters/ConverseParameters.swift (100%) rename Sources/{ => BedrockService}/Parameters/EmbeddingsParameters.swift (100%) rename Sources/{ => BedrockService}/Parameters/ImageGenerationParameters.swift (100%) rename Sources/{ => BedrockService}/Parameters/ParameterName.swift (100%) rename Sources/{ => BedrockService}/Parameters/Parameters.swift (100%) rename Sources/{ => BedrockService}/Parameters/TextGenerationParameters.swift (100%) rename Sources/{ => BedrockService}/Protocols/BedrockClientProtocol.swift (100%) rename Sources/{ => BedrockService}/Protocols/BedrockConfigProtocol.swift (100%) rename Sources/{ => BedrockService}/Protocols/BedrockRuntimeClientProtocol.swift (100%) rename Sources/{ => BedrockService}/Region.swift (100%) diff --git a/.gitignore b/.gitignore index ac3515cc..c9ec5d7e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ Package.resolved Makefile **/temp node_modules +docc-output # **/backend **/backend/.DS_Store diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 00000000..b4a7c6e0 --- /dev/null +++ b/.spi.yml @@ -0,0 +1,4 @@ +version: 1 +builder: + configs: + - documentation_targets: [BedrockService] diff --git a/Package.swift b/Package.swift index 35059e4c..cbb0967b 100644 --- a/Package.swift +++ b/Package.swift @@ -15,6 +15,7 @@ let package = Package( .package(url: "https://github.com/smithy-lang/smithy-swift", from: "0.158.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.6.4"), .package(url: "https://github.com/awslabs/aws-crt-swift", from: "0.53.0"), + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), ], targets: [ .target( diff --git a/README.md b/README.md index 77579713..2b31e3b2 100644 --- a/README.md +++ b/README.md @@ -1,1224 +1,68 @@ # Swift Bedrock Library -A tiny layer on top of the [AWS SDK for Swift](https://github.com/awslabs/aws-sdk-swift) for interacting with [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) foundation models. This library provides a convenient way to access Amazon Bedrock's capabilities from Swift applications. +A Swift library for interacting with [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) foundation models. ## Acknowledgment This library and playground have been written by [Mona Dierickx](https://www.linkedin.com/in/mona-dierickx/), during her last year of studies at [HoGent](https://www.hogent.be/), Belgium. -Thank you for your enthousiasm and positive attitude during the three months we worked together. (February 2025 - May 2025). +Thank you for your enthusiasm and positive attitude during the three months we worked together (February 2025 - May 2025). -Thank for Professor Steven Van Impe for allowing us to work with these young talents. +Thank you Professor Steven Van Impe for allowing us to work with these young talents. -## Getting started with BedrockService +## TL;DR - Quick Start -1. Set-up your `Package.swift` - -First add dependencies: -```bash -swift package add-dependency https://github.com/build-on-aws/swift-bedrock-library.git --branch main -swift package add-target-dependency BedrockService TargetName --package swift-bedrock-library -``` - -Next up add `platforms` configuration after `name` - -```swift -platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], -``` - -Your `Package.swift` should now look something like this: ```swift -import PackageDescription - -let package = Package( - name: "ProjectName", - platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], - dependencies: [ - .package(url: "https://github.com/build-on-aws/swift-bedrock-library.git", branch: "main"), - ], - targets: [ - .executableTarget( - name: "TargetName", - dependencies: [ - .product(name: "BedrockService", package: "swift-bedrock-library"), - ] - ) - ] -) -``` - -2. Import the BedrockService - -```swift import BedrockService -``` - -3. Initialize the BedrockService - -Choose what Region to use, whether to use AWS SSO authentication instead of standard credentials and pass a logger. If no region is passed it will default to `.useast1`, if no logger is provided a default logger with the name `bedrock.service` is created. The log level will be set to the environment variable `BEDROCK_SERVICE_LOG_LEVEL` or default to `.trace`. Choose the form of authentication you wish to use. - -```swift -let bedrock = try await BedrockService( - region: .uswest1, - logger: logger, - authentication: .sso -) -``` - -4. List the available models - -Use the `listModels()` function to test your set-up. This function will return an array of `ModelSummary` objects, each one representing a model supported by Amazon Bedrock. The ModelSummaries that contain a `BedrockModel` object are the models supported by BedrockService. - -```swift -let models = try await bedrock.listModels() -``` - -## Authentication - -The Swift Bedrock Library supports multiple authentication methods to work with Amazon Bedrock. By default, it uses the standard AWS credential provider chain, but you can specify different authentication types when initializing the `BedrockService`. - -### Default Authentication - -Uses the standard AWS credential provider chain, which checks for credentials in the following order: -1. Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) -2. AWS credentials file (`~/.aws/credentials`) -3. AWS config file (`~/.aws/config`) -4. IAM roles for Amazon EC2 instances -5. IAM roles for tasks (Amazon ECS) -6. IAM roles for Lambda functions - -```swift -let bedrock = try await BedrockService( - region: .uswest2 - // authentication defaults to .default -) -``` - -### Profile-based Authentication - -Use a specific profile from your AWS credentials file. This is useful when you have multiple AWS accounts or roles configured locally. - -```swift -let bedrock = try await BedrockService( - region: .uswest2, - authentication: .profile(profileName: "my-profile") -) -``` - -### SSO Authentication - -Use AWS Single Sign-On (SSO) authentication. You must run `aws sso login --profile ` before using this authentication method. - -```swift -let bedrock = try await BedrockService( - region: .uswest2, - authentication: .sso(profileName: "my-sso-profile") -) -``` - -### Web Identity Token Authentication - -Use a JWT token from an external identity provider (like Sign In with Apple or Google) to assume an IAM role. This is particularly useful for iOS, tvOS, and macOS applications where traditional AWS CLI-based authentication isn't available. - -```swift -let bedrock = try await BedrockService( - region: .uswest2, - authentication: .webIdentity( - token: jwtToken, - roleARN: "arn:aws:iam::123456789012:role/MyAppRole", - region: .uswest2, - notification: { - // Optional: Called on main thread when credentials are retrieved - print("AWS credentials updated") - } - ) -) -``` - -### API Key Authentication -Use an API key for authentication. API keys are generated in the AWS console and provide a simpler authentication method for specific use cases. +// Initialize the service +let bedrock = try await BedrockService(region: .uswest2) -```swift -let bedrock = try await BedrockService( - region: .uswest2, - authentication: .apiKey(key: "your-api-key-here") -) -``` - -As usual, do not store or hardcode API Keys in your front end application. - -### Static Credentials Authentication - -Use static AWS credentials directly. **This method is strongly discouraged for production use** and should only be used for testing and debugging purposes. - -```swift -let bedrock = try await BedrockService( - region: .uswest2, - authentication: .static( - accessKey: "AKIAIOSFODNN7EXAMPLE", - secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", - sessionToken: "optional-session-token" - ) -) -``` - -**Security Note**: Never hardcode credentials in your source code or commit them to version control. Use environment variables, secure credential storage, or other secure methods to manage credentials in production applications. - -## Chatting using the Converse or ConverseStream API - -### Text prompt - -To send a text prompt to a model, first choose a model that supports converse, you can verify this by using the `hasConverseModality` function on the `BedrockModel`. Then use the model to create a `ConverseRequestBuilder`, add your prompt to it with the `.withPrompt` function. Use the builder to send your request to the Converse API with the `converse` function. You can then easily print the reply and use it to create a new builder with the same model and inference parameters but with an updated history. - -```swift +// Send a simple text prompt let model: BedrockModel = .nova_lite - -guard model.hasConverseModality() else { - throw MyError.incorrectModality("\(model.name) does not support converse") -} - -var builder = try ConverseRequestBuilder(with: model) - .withPrompt("Tell me about rainbows") - -var reply = try await bedrock.converse(with: builder) - -print("Assistant: \(reply)") - -builder = try ConverseRequestBuilder(from: builder, with: reply) - .withPrompt("Do you think birds can see them too?") - -reply = try await bedrock.converse(with: builder) - -print("Assistant: \(reply)") -``` - -Optionally add inference parameters. Note that the builder can be used to create the next builder with the same parameters and the updated history. - -```swift let builder = try ConverseRequestBuilder(with: model) .withPrompt("Tell me about rainbows") - .withMaxTokens(512) - .withTemperature(0.2) - .withStopSequences(["END", "STOP", ""]) - .withSystemPrompts(["Do not pretend to be human", "Never talk about goats", "You like puppies"]) - -var reply = try await bedrock.converse(with: builder) - -builder = try ConverseRequestBuilder(from: builder, with: reply) - .withPrompt("Do you think birds can see them too?") - -reply = try await bedrock.converse(with: builder) -``` - -To get a streaming response, use the same `ConverseRequestBuilder`, but the `converseStream` function instead of the `converse` function. Ensure the model you are using supports streaming. -The stream will contain a `.stream` of `ConverseStreamElement` objects that indicate the progress into the response. - -To create the next builder, with the same model and inference parameters, use the full message from the `.messageComplete`. The `ConverseStreamElement` enum provides several cases to track the streaming response: - -- `.messageStart(Role)`: Indicates the beginning of a message with the specified role (assistant, user, etc.) -- `.text(Int, String)`: Contains partial text content with an index and the text fragment -- `.reasoning(Int, String)`: Contains partial reasoning content with an index and the reasoning fragment -- `.toolUse(Int, ToolUseBlock)`: Contains a complete tool use response with an index and the tool use details -- `.messageComplete(Message)`: Provides the complete message with all content blocks and reason for stopping -- `.metaData(ResponseMetadata)`: Contains metadata about the response including token usage and latency metrics - -The index in each case helps track the order of content blocks in the final message. When processing a stream, you should handle each element type appropriately. For convenience, the `messageComplete` event contains the full response, ready to use. - -The stream provided by this library is a balance between convenience of use and latency. If you need more flexibility or very low latency (for example, no buffering on the tool use response), the `ConverseStreamreply` also exposes the low-level stream returned by the AWS SDK. - -```swift - let model: BedrockModel = .nova_lite - - guard model.hasConverseModality() else { - throw MyError.incorrectModality("\(model.name) does not support converse") - } - - // create a request - let builder = try ConverseRequestBuilder(with: model) - .withPrompt("Tell me about rainbows") - - // send the request - let reply = try await bedrock.converseStream(with: builder) - - // consume the stream of elements - for try await element in reply.stream { - - switch element { - case .messageStart(let role): - logger.info("Message started with role: \(role)") - - case .text(_, let text): - print(text, terminator: "") - - case .reasoning(let index, let reasoning): - logger.info("Reasoning delta: \(reasoning)", metadata: ["index": "\(index)"]) - - case .toolUse(let index, let toolUse): - logger.info( - "Tool use: \(toolUse.name) with id: \(toolUse.id) and input: \(toolUse.input)", - metadata: ["index": "\(index)"] - ) - - case .messageComplete(_): - print("\n") - - case .metaData(let metaData): - logger.info("Metadata: \(metaData)") - } - } -``` - -### Vision - -To send an image to a model, first ensure the model supports vision. Next simply add the image to the `ConverseRequestBuilder` with the `withImage` function. The function can either take an `ImageBlock` object or the format and bytes to construct the object. - - -```swift -let model: BedrockModel = .nova_lite - -guard model.hasConverseModality(.vision) else { - throw MyError.incorrectModality("\(model.name) does not support converse vision") -} - -let builder = try ConverseRequestBuilder(with: model) - .withPrompt("Can you tell me about this plant?") - .withImage(format: .jpeg, source: base64EncodedImage) let reply = try await bedrock.converse(with: builder) - print("Assistant: \(reply)") ``` -Optionally add inference parameters. +## Installation -```swift -let builder = try ConverseRequestBuilder(with: model) - .withPrompt("Can you tell me about this plant?") - .withImage(format: .jpeg, source: base64EncodedImage) - .withTemperature(0.8) - -let reply = try await bedrock.converse(with: builder) -``` - -Note that the builder can be used to create the next builder with the same parameters and the updated history. +Add to your `Package.swift`: ```swift -var builder = try ConverseRequestBuilder(with: model) - .withPrompt("Can you tell me about this plant?") - .withImage(format: .jpeg, source: base64EncodedImage) - .withTemperature(0.8) - -var reply = try await bedrock.converse(with: builder) - -builder = try ConverseRequestBuilder(from: builder, with: reply) - .withPrompt("Where can I find those plants?") - -reply = try await bedrock.converse(with: builder) -``` - -To use streaming use the exact same `ConverseRequestBuilder`, but use the `converseStream` function instead of the `converse` function. An example is given in the [text prompt section](#text-prompt). - -### Document - -To send a document to a model, first ensure the model supports document. Next simply add the document to the `ConverseRequestBuilder` with the `withDocument` function. The function can either take a `DocumentBlock` object or the name, format and bytes to construct the object. - -```swift -let model: BedrockModel = .nova_lite - -guard model.hasConverseModality(.document) else { - throw MyError.incorrectModality("\(model.name) does not support converse document") -} - -let builder = try ConverseRequestBuilder(with: model) - .withPrompt("Can you give me a summary of this chapter?") - .withDocument(name: "Chapter 1", format: .pdf, source: base64EncodedDocument) - -let reply = try await bedrock.converse(with: builder) - -print("Assistant: \(reply)") -``` - -Optionally add inference parameters. - -```swift -let builder = try ConverseRequestBuilder(with: model) - .withPrompt("Can you give me a summary of this chapter?") - .withDocument(name: "Chapter 1", format: .pdf, source: base64EncodedDocument) - .withMaxTokens(512) - .withTemperature(0.4) - -var reply = try await bedrock.converse(with: builder) -``` - -Note that the builder can be used to create the next builder with the same parameters and the updated history. - -```swift -var builder = try ConverseRequestBuilder(with: model) - .withPrompt("Can you give me a summary of this chapter?") - .withDocument(name: "Chapter 1", format: .pdf, source: base64EncodedDocument) - .withMaxTokens(512) - .withTemperature(0.4) - -var reply = try await bedrock.converse(with: builder) - -builder = try ConverseRequestBuilder(from: builder, with: reply) - .withPrompt("Thanks, can you make a Dutch version as well?") - -reply = try await bedrock.converse(with: builder) -``` - -To use streaming use the exact same `ConverseRequestBuilder`, but use the `converseStream` function instead of the `converse` function. An example is given in the [text prompt section](#text-prompt). - -### Tools - -For tool usage, first ensure the model supports the use of tools. Next define at least one `Tool` and add it to the `ConverseRequestBuilder` with the `withTool` function (or the `withTools` function to add several tools at once). After sending a request the model could now send back a `ToolUse` asking for specific information from a specific tool. Use this to send the information back in a `ToolResult`, by using the `withToolResult` function. You will now receive a reply informed by the result from the tool. - - -```swift -let model: BedrockModel = .nova_lite - -// verify that the model supports tool usage -guard model.hasConverseModality(.toolUse) else { - throw MyError.incorrectModality("\(model.name) does not support converse tools") -} - -// define the inputschema for your tool -let inputSchema = JSON([ - "type": "object", - "properties": [ - "sign": [ - "type": "string", - "description": "The call sign for the radio station for which you want the most popular song. Example calls signs are WZPZ and WKRP." - ] - ], - "required": [ - "sign" - ] -]) - -// create a Tool object -let tool = try Tool(name: "top_song", inputSchema: inputSchema, description: "Get the most popular song played on a radio station.") - -// create a ConverseRequestBuilder with a prompt and the Tool object -var builder = try ConverseRequestBuilder(with: model) - .withPrompt("What is the most popular song on WZPZ?") - .withTool(tool) - -// pass the ConverseRequestBuilder object to the converse function -var reply = try await bedrock.converse(with: builder) - -if let toolUse = try? reply.getToolUse() { - let id = toolUse.id - let name = toolUse.name - let input = toolUse.input - - // ... Logic to use the tool here ... - - // Send the toolResult back to the model - builder = try ConverseRequestBuilder(from: builder, with: reply) - .withToolResult("The Best Song Ever") // pass any Codable or Data - - reply = try await bedrock.converse(with: builder) -} - -print("Assistant: \(reply)") -// The final reply will be similar to: "The most popular song currently played on WZPZ is \"The Best Song Ever\". If you need more information or have another request, feel free to ask!" -``` - -To use streaming use the exact same `ConverseRequestBuilder`, but use the `converseStream` function instead of the `converse` function. - -```swift -let bedrock = try await BedrockService(authentication: .sso()) -let model: BedrockModel = .claudev3_7_sonnet - -// define the inputschema for your tool -let schema = JSON(with: [ - "type": "object", - "properties": [ - "sign": [ - "type": "string", - "description": - "The call sign for the radio station for which you want the most popular song. Example calls signs are WZPZ, StuBru and Klara.", +dependencies: [ + .package(url: "https://github.com/build-on-aws/swift-bedrock-library.git", branch: "main") +], +targets: [ + .target( + name: "YourTarget", + dependencies: [ + .product(name: "BedrockService", package: "swift-bedrock-library") ] - ], - "required": [ - "sign" - ], -]) - -// pass a prompt and the tool to converse -var builder = try ConverseRequestBuilder(with: model) - .withPrompt("Introduce yourself and mention the tools you have access to?") - .withTool( - name: "top_song", - inputSchema: schema, - description: "Get the most popular song played on a radio station." ) - -var stream: AsyncThrowingStream -var assistantMessage: Message = Message("empty") - -// start a loop to interact with the user -while true { - var prompt: String = "" - var indexes: [Int] = [] - var toolRequests: [ToolUseBlock] = [] - - // create the stream by calling the converseStream function - stream = try await bedrock.converseStream(with: builder) - - // process the stream - for try await element in stream { - switch element { - case .contentSegment(let contentSegment): - switch contentSegment { - case .text(let index, let text): - if !indexes.contains(index) { - indexes.append(index) - print("\nAssistant: ") - } - print(text, terminator: "") - default: - break - } - case .contentBlockComplete(_, let content): - print("\n") - if case .toolUse(let toolUse) = content { - toolRequests.append(toolUse) - } - case .messageComplete(let message): - assistantMessage = message - } - } - - // if a request to use a tool was made by the model, use the information in the input to return the correct information back to the model in a ToolResultBlock - if !toolRequests.isEmpty { - for toolUse in toolRequests { - print("found tool use") - print(toolUse) - if toolUse.name == "top_song" { - let sign: String? = toolUse.input["sign"] - if let sign { - let song = try await getMostPopularSong(sign: sign) - builder = try ConverseRequestBuilder(from: builder, with: assistantMessage) - .withToolResult(song) - } - } - } - } else { - // if no request to use a tool was made, no ToolResultBlock needs to be returned and the user can ask the next question - print("\nYou: ") - prompt = readLine()! - if prompt == "done" { - break - } - - builder = try ConverseRequestBuilder(from: builder, with: assistantMessage) - .withPrompt(prompt) - } -} +] ``` -### Reasoning - -To not only get a text reply but to also follow the model's reasoning, enable reasoning by using the `withReasoning` and optionally set the maximum length of the reasoning with `withMaxReasoningTokens`. These functions can be combined using the `withReasoning(maxReasoningTokens: Int)` function. - -```swift -let model: BedrockModel = .claudev3_7_sonnet +Requires: `platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)]` -guard model.hasConverseModality() else { - throw MyError.incorrectModality("\(model.name) does not support converse") -} -guard model.hasConverseModality(.reasoning) else { - throw MyError.incorrectModality("\(model.name) does not support reasoning") -} +## Documentation -var prompt = "Introduce yourself in one sentence" +📖 **[Complete Documentation](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation)** - Comprehensive guides and API reference -var builder = try ConverseRequestBuilder(with: model) - .withPrompt(prompt) - .withReasoning() - .withMaxReasoningTokens(1024) // Optional +Key topics: +- [Authentication](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation/bedrockservice/authentication) - Configure AWS credentials +- [Converse API](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation/bedrockservice/converse) - Conversational AI +- [Image Generation](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation/bedrockservice/imagegeneration) - Create and modify images +- [Tools](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation/bedrockservice/tools) - Function calling +- [Streaming](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation/bedrockservice/streaming) - Real-time responses -var reply = try await bedrock.converse(with: builder) - -if let reasoning = try? reply.getReasoningBlock() { - print("\nReasoning: \(reasoning.reasoning)") -} -print("\nAssistant: \(reply)") -``` - -To combine reasoning and streaming, use the same `ConverseRequestBuilder`, but use the `converseStream` function instead of the `converse` function. A `ContentSegment` can then contain `reasoning`. - -```swift -let model: BedrockModel = .claudev3_7_sonnet +## Examples -guard model.hasConverseModality() else { - throw MyError.incorrectModality("\(model.name) does not support converse") -} -guard model.hasConverseModality(.streaming) else { - throw MyError.incorrectModality("\(model.name) does not support streaming") -} -guard model.hasConverseModality(.reasoning) else { - throw MyError.incorrectModality("\(model.name) does not support reasoning") -} - -var builder = try ConverseRequestBuilder(from: builder, with: reply) - .withPrompt("Tell me more about the birds in Paris") - .withReasoning(maxReasoningTokens: 1024) - -let stream = try await bedrock.converseStream(with: builder) - -var indexes: [Int] = [] - -for try await element in stream { - switch element { - case .contentSegment(let contentSegment): - switch contentSegment { - case .text(let index, let text): - if !indexes.contains(index) { - indexes.append(index) - print("\nAssistant: ") - } - print(text, terminator: "") - case .reasoning(let index, let text, _): - if !indexes.contains(index) { - indexes.append(index) - print("\nReasoning: ") - } - print(text, terminator: "") - default: - break - } - case .contentBlockComplete: - print("\n\n") - case .messageComplete(let message): - assistantMessage = message - } -} - -builder = try ConverseRequestBuilder(from: builder, with: assistantMessage) - .withPrompt("And what about the rats?") -``` - -### Make your own `Message` - -Alternatively use the `converse` function that does not take a `prompt`, `toolResult` or `image` and construct the `Message` yourself. - -```swift -// Message with prompt -let replyMessage = try await bedrock.converse( - with: model, - conversation: [Message("What day of the week is it?")] -) - -// Optionally add inference parameters -let replyMessage = try await bedrock.converse( - with: model, - conversation: [Message("What day of the week is it?")], - maxTokens: 512, - temperature: 1, - topP: 0.8, - stopSequences: ["THE END"], - systemPrompts: ["Today is Wednesday, make sure to mention that."] -) - -// Message with an image and prompt -let replyMessage = try await bedrock.converse( - with: model, - conversation: [Message("What is in the this teacup?", imageFormat: .jpeg, imageBytes: base64EncodedImage)], -) - -// Message with toolResult -let replyMessage = try await bedrock.converse( - with: model, - conversation: [Message(toolResult)], - tools: [toolA, toolB] -) -``` - -### JSON - -The `JSON` struct is a lightweight and flexible wrapper for working with JSON-like data in Swift. It provides convenient methods and initializers to parse, access, and manipulate JSON data while maintaining type safety and versatility. - -#### Creating a JSON Object - -You can create a `JSON` object by wrapping raw values or constructing nested structures: -```swift -let json = JSON([ - "name": JSON("Jane Doe"), - "age": JSON(30), - "isMember": JSON(true), -]) -``` -#### Creating JSON object from String - -The `JSON` struct provides an initializer to parse valid JSON strings into a `JSON` object: - -```swift -let validJSONString = """ -{ - "name": "Jane Doe", - "age": 30, - "isMember": true -} -""" - -do { - let json = try JSON(from: validJSONString) - print(json.getValue("name") ?? "No name") // Output: Jane Doe -} catch { - print("Failed to parse JSON:", error) -} -``` - -#### Accessing values using `getValue` - -The `getValue(_ key: String)` method retrieves values of the specified type from the JSON object: - -```swift -if let name: String? = json.getValue("name") { - print("Name:", name) // Output: Name: Jane Doe -} - -if let age: Int? = json.getValue("age") { - print("Age:", age) // Output: Age: 30 -} - -if let isMember: Bool? = json.getValue("isMember") { - print("Is Member:", isMember) // Output: Is Member: true -} -``` - -#### Accessing values using subscripts - -You can also access values dynamically using subscripts: - -```swift -let name: String? = json["name"] -print("Name:", name ?? "Unknown") // Output: Name: Jane Doe - -let nonExistent: String? = json["nonExistentKey"] -print(nonExistent == nil) // Output: true -``` - -Note that the subscript method is also able to handle nested objects. - -```swift -let json = JSON([ - "name": JSON("Jane Doe"), - "age": JSON(30), - "isMember": JSON(true), - "address": JSON([ - "street": JSON("123 Main St"), - "city": JSON("Anytown"), - "postalCode": JSON(12345), - ]), -]) - -let street: String = json["address"]?["street"] -print("Street:", name ?? "Unknown") // Street: 123 Main St -``` - -## Generating an image using the InvokeModel API - -Choose a BedrockModel that supports image generation - you can verify this using the `hasImageModality` and the `hasTextToImageModality` function. The `generateImage` function allows you to create images from text descriptions with various optional parameters: - -- `prompt`: Text description of the desired image -- `negativePrompt`: Text describing what to avoid in the generated image -- `nrOfImages`: Number of images to generate -- `cfgScale`: Classifier free guidance scale to control how closely the image follows the prompt -- `seed`: Seed for reproducible image generation -- `quality`: Parameter to control the quality of generated images -- `resolution`: Desired image resolution for the generated images - -The function returns an ImageGenerationOutput object containing an array of generated images in base64 format. - -```swift -let model: BedrockModel = .nova_canvas - -guard model.hasImageModality(), - model.hasTextToImageModality() else { - throw MyError.incorrectModality("\(model.name) does not support image generation") -} - -let imageGeneration = try await bedrock.generateImage( - "A serene landscape with mountains at sunset", - with: model -) -``` - -Optionally add inference parameters. - -```swift -let imageGeneration = try await bedrock.generateImage( - "A serene landscape with mountains at sunset", - with: model, - negativePrompt: "dark, stormy, people", - nrOfImages: 3, - cfgScale: 7.0, - seed: 42, - quality: .standard, - resolution: ImageResolution(width: 100, height: 100) -) -``` - -Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. - -## Generating image variations using the InvokeModel API -Choose a BedrockModel that supports image variations - you can verify this using the `hasImageModality` and the `hasImageVariationModality` function. The `generateImageVariation` function allows you to create variations of an existing image with these parameters: - -- `images`: The base64-encoded source images used to create variations from -- `negativePrompt`: Text describing what to avoid in the generated image -- `similarity`: Controls how similar the variations will be to the source images -- `nrOfImages`: Number of variations to generate -- `cfgScale`: Classifier free guidance scale to control how closely variations follow the original image -- `seed`: Seed for reproducible variation generation -- `quality`: Parameter to control the quality of generated variations -- `resolution`: Desired resolution for the output variations - -This function returns an `ImageGenerationOutput` object containing an array of generated image variations in base64 format. Each variation will maintain key characteristics of the source images while introducing creative differences. - -```swift -let model: BedrockModel = .nova_canvas - -guard model.hasImageVariationModality(), - model.hasImageVariationModality() else { - throw MyError.incorrectModality("\(model.name) does not support image variation generation") -} - -let imageVariations = try await bedrock.generateImageVariation( - images: [base64EncodedImage], - prompt: "A dog drinking out of this teacup", - with: model -) -``` - -Optionally add inference parameters. - -```swift -let imageVariations = try await bedrock.generateImageVariation( - images: [base64EncodedImage], - prompt: "A dog drinking out of this teacup", - with: model, - negativePrompt: "Cats, worms, rain", - similarity: 0.8, - nrOfVariations: 4, - cfgScale: 7.0, - seed: 42, - quality: .standard, - resolution: ImageResolution(width: 100, height: 100) -) -``` - -Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. - -## Generating text using the InvokeModel API - -Choose a BedrockModel that supports text generation, you can verify this using the `hasTextModality` function. When calling the `completeText` function you can provide some inference parameters: - -- `maxTokens`: The maximum amount of tokens that the model is allowed to return -- `temperature`: Controls the randomness of the model's output -- `topP`: Nucleus sampling, this parameter controls the cumulative probability threshold for token selection -- `topK`: Limits the number of tokens the model considers for each step of text generation to the K most likely ones -- `stopSequences`: An array of strings that will cause the model to stop generating further text when encountered - -The function returns a `TextCompletion` object containing the generated text. - -```swift -let model: BedrockModel = .nova_micro - -guard model.hasTextModality() else { - throw MyError.incorrectModality("\(model.name) does not support text generation") -} - -let textCompletion = try await bedrock.completeText( - "Write a story about a space adventure", - with: model -) - -print(textCompletion.completion) -``` - -Optionally add inference parameters. - -```swift -let textCompletion = try await bedrock.completeText( - "Write a story about a space adventure", - with: model, - maxTokens: 1000, - temperature: 0.7, - topP: 0.9, - topK: 250, - stopSequences: ["THE END"] -) -``` - -Note that the minimum, maximum and default values for each parameter are model specific and defined when the BedrockModel is created. Some parameters might not be supported by certain models. - -## Generating embeddings using the InvokeModel API - -Choose a BedrockModel that supports embeddings generation - you can verify this using the `hasEmbeddingsModality` function. The `embed` function allows you to convert text into numerical vector representations that capture semantic meaning: - -```swift -let model: BedrockModel = .titan_embed_text_v1 - -let embeddings = try await bedrock.embed( - "Swift is a powerful programming language", - with: model -) - -print("Generated embeddings with \(embeddings.count) dimensions") -``` - -Optionally specify the vector size: - -```swift -let embeddings = try await bedrock.embed( - "Swift is a powerful programming language", - with: model, - vectorSize: 512 -) -``` - -Embeddings are returned as an array of Double values (`Embeddings` typealias) that can be used for: -- Semantic similarity comparisons -- Text clustering and classification -- Retrieval-augmented generation (RAG) systems -- Machine learning feature vectors - -Note that the available vector sizes and other parameters are model specific and defined when the BedrockModel is created. - -## How to add a BedrockModel - -### Converse - -To add a new model that only needs the ConverseModality, simply use the `StandardConverse` and add the correct [inference parameters](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html) and [supported converse features](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html). - -```swift -extension BedrockModel { - public static let new_bedrock_model = BedrockModel( - id: "family.model-id-v1:0", - name: "New Model Name", - modality: StandardConverse( - parameters: ConverseParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), - maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), - topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), - stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), - maxPromptSize: nil - ), - features: [.textGeneration, .systemPrompts, .document, .toolUse] - ) - ) -} -``` - -If the model also implements other modalities you might need to create you own `Modality` and make sure it conforms to `ConverseModality` by implementing the `getConverseParameters` and `getConverseFeatures` functions. Note that the `ConverseParameters` can be extracted from `TextGenerationParameters` by using the public initializer. - -```swift -struct ModelFamilyModality: TextModality, ConverseModality { - func getName() -> String { "Model Family Text and Converse Modality" } - - let parameters: TextGenerationParameters - let converseFeatures: [ConverseFeature] - let converseParameters: ConverseParameters - - init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration]) { - self.parameters = parameters - self.converseFeatures = features - - // public initializer to extract `ConverseParameters` from `TextGenerationParameters` - self.converseParameters = ConverseParameters(textGenerationParameters: parameters) - } - - // ... -} -``` - -### Text - -If you need to add a model from a model family that is not supported at all by the library, follow these steps: - -#### Step 1: Create family-specific request and response struct - -Make sure to create a struct that reflects exactly how the body of the request for an invokeModel call to this family should look. Make sure to add the public initializer with parameters `prompt`, `maxTokens` and `temperature` to comply to the `BedrockBodyCodable` protocol. Take a look at the documentation to apply best practices or specific formatting. - -```json -{ - "prompt": "\(prompt)", - "temperature": 1, - "top_p": 0.9, - "max_tokens": 200, - "stop": ["END"] -} -``` - -```swift -public struct LlamaRequestBody: BedrockBodyCodable { - let prompt: String - let max_gen_len: Int - let temperature: Double - let top_p: Double - - public init(prompt: String, maxTokens: Int = 512, temperature: Double = 0.5) { - self.prompt = - "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\(prompt)<|eot_id|><|start_header_id|>assistant<|end_header_id|>" - self.max_gen_len = maxTokens - self.temperature = temperature - self.top_p = 0.9 - } -} -``` - -Do the same for the response and ensure to add the `getTextCompletion` method to extract the completion from the response body and to comply to the `ContainsTextCompletion` protocol. - -```json -{ - "generation": "\n\n", - "prompt_token_count": int, - "generation_token_count": int, - "stop_reason" : string -} -``` - -```swift -struct LlamaResponseBody: ContainsTextCompletion { - let generation: String - let prompt_token_count: Int - let generation_token_count: Int - let stop_reason: String - - public func getTextCompletion() throws -> TextCompletion { - TextCompletion(generation) - } -} -``` - -#### Step 2: Create the Modality - -For a text generation create a struct conforming to TextModality. Use the request body and response body you created in [the previous step](#step-1-create-family-specific-request-and-response-struct). Make sure to check for model(family) specific rules or parameters that are not supported here. - -```swift -struct LlamaText: TextModality { - let parameters: TextGenerationParameters - - init(parameters: TextGenerationParameters) { - self.parameters = parameters - } - - func getName() -> String { "Llama Text Generation" } - - func getParameters() -> TextGenerationParameters { - parameters - } - - func getTextRequestBody( - prompt: String, - maxTokens: Int?, - temperature: Double?, - topP: Double?, - topK: Int?, - stopSequences: [String]? - ) throws -> BedrockBodyCodable { - guard topK == nil else { - throw BedrockLibraryError.notSupported("TopK is not supported for Llama text completion") - } - guard stopSequences == nil else { - throw BedrockLibraryError.notSupported("stopSequences is not supported for Llama text completion") - } - return LlamaRequestBody( - prompt: prompt, - maxTokens: maxTokens ?? parameters.maxTokens.defaultValue, - temperature: temperature ?? parameters.temperature.defaultValue, - topP: topP ?? parameters.topP.defaultValue - ) - } - - func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { - let decoder = JSONDecoder() - return try decoder.decode(LlamaResponseBody.self, from: data) - } -} -``` - -#### Step 3: Create BedrockModel instance - -You can now create instances for any of the models that follow the request and response structure you defined. Make sure to check the allowed and default values for the inference parameters, especially if some parameters are not supported by the model. Know that these parameters may differ significantly for models from the same family. - -```swift -extension BedrockModel { - public static let llama3_3_70b_instruct: BedrockModel = BedrockModel( - id: "meta.llama3-3-70b-instruct-v1:0", - name: "Llama 3.3 70B Instruct", - modality: LlamaText( - parameters: TextGenerationParameters( - temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), - maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), - topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), - topK: Parameter.notSupported(.topK), - stopSequences: StopSequenceParams.notSupported(), - maxPromptSize: nil - ) - ) - ) -} -``` - -### Image - -To add an image generation model from a model family that is not supported at all by the library, the steps are much alike to the text completion models. - -#### Step 1: Create family-specific request and response struct - -Make sure to create a struct that reflects exactly how the body of the request for an invokeModel call to this family should look. Take a look at the documentation to apply best practices or specific formatting. - -```swift -public struct AmazonImageRequestBody: BedrockBodyCodable { - let taskType: TaskType - private let textToImageParams: TextToImageParams? - private let imageGenerationConfig: ImageGenerationConfig - - // MARK: - Initialization - - /// Creates a text-to-image generation request body - /// - Parameters: - /// - prompt: The text description of the image to generate - /// - nrOfImages: The number of images to generate - /// - negativeText: The text description of what to exclude from the generated image - /// - Returns: A configured AmazonImageRequestBody for text-to-image generation - public static func textToImage( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) -> Self { - AmazonImageRequestBody( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } - - private init( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) { - self.taskType = .textToImage - self.textToImageParams = TextToImageParams.textToImage(prompt: prompt, negativeText: negativeText) - self.imageGenerationConfig = ImageGenerationConfig( - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } -} -``` - -Do the same for the response and ensure to add the `getGeneratedImage` method to extract the image from the response body and to comply to the `ContainsImageGeneration` protocol. - -```swift -public struct AmazonImageResponseBody: ContainsImageGeneration { - let images: [Data] - - public func getGeneratedImage() -> ImageGenerationOutput { - ImageGenerationOutput(images: images) - } -} -``` - -#### Step 2: Create the Modality - -Determine the exact functionality and make sure to comply to the correct modality protocol. In this case we will use `TextToImageModality`. -Create a struct conforming to `ImageModality` and the specific functionality protocol. Use the request body and response body you created in [the previous step](#step-1-create-family-specific-request-and-response-struct). Make sure to check for model(family) specific rules or parameters that are not supported here. - -```swift -struct AmazonImage: ImageModality, TextToImageModality { - func getName() -> String { "Amazon Image Generation" } - - let parameters: ImageGenerationParameters - let resolutionValidator: any ImageResolutionValidator - let textToImageParameters: TextToImageParameters - - init( - parameters: ImageGenerationParameters, - resolutionValidator: any ImageResolutionValidator, - textToImageParameters: TextToImageParameters - ) { - self.parameters = parameters - self.textToImageParameters = textToImageParameters - self.conditionedTextToImageParameters = conditionedTextToImageParameters - self.imageVariationParameters = imageVariationParameters - self.resolutionValidator = resolutionValidator - } - - func getParameters() -> ImageGenerationParameters { parameters } - func getTextToImageParameters() -> TextToImageParameters { textToImageParameters } - - func validateResolution(_ resolution: ImageResolution) throws { - try resolutionValidator.validateResolution(resolution) - } - - func getImageResponseBody(from data: Data) throws -> ContainsImageGeneration { - let decoder = JSONDecoder() - return try decoder.decode(AmazonImageResponseBody.self, from: data) - } - - func getTextToImageRequestBody( - prompt: String, - negativeText: String?, - nrOfImages: Int?, - cfgScale: Double?, - seed: Int?, - quality: ImageQuality?, - resolution: ImageResolution? - ) throws -> BedrockBodyCodable { - AmazonImageRequestBody.textToImage( - prompt: prompt, - negativeText: negativeText, - nrOfImages: nrOfImages, - cfgScale: cfgScale, - seed: seed, - quality: quality, - resolution: resolution - ) - } -} -``` - -#### Step 3: Create BedrockModel instance - -You can now create instances for any of the models that follow the request and response structure you defined. Make sure to check the allowed and default values for the inference parameters, especially if some parameters are not supported by the model. Know that these parameters may differ significantly for models from the same family. - -```swift -extension BedrockModel { - public static let nova_canvas: BedrockModel = BedrockModel( - id: "amazon.nova-canvas-v1:0", - name: "Nova Canvas", - modality: AmazonImage( - parameters: ImageGenerationParameters( - nrOfImages: Parameter(.nrOfImages, minValue: 1, maxValue: 5, defaultValue: 1), - cfgScale: Parameter(.cfgScale, minValue: 1.1, maxValue: 10, defaultValue: 6.5), - seed: Parameter(.seed, minValue: 0, maxValue: 858_993_459, defaultValue: 12) - ), - resolutionValidator: NovaImageResolutionValidator(), - textToImageParameters: TextToImageParameters(maxPromptSize: 1024, maxNegativePromptSize: 1024), - ) - ) -} -``` +Explore the [Examples](./Examples/) directory for complete sample applications including: +- Basic conversation chat +- Streaming responses +- Image generation +- iOS math solver app +- Web playground with frontend/backend \ No newline at end of file diff --git a/Sources/BedrockAuthentication+JWT.swift b/Sources/BedrockService/BedrockAuthentication+JWT.swift similarity index 100% rename from Sources/BedrockAuthentication+JWT.swift rename to Sources/BedrockService/BedrockAuthentication+JWT.swift diff --git a/Sources/BedrockAuthentication.swift b/Sources/BedrockService/BedrockAuthentication.swift similarity index 100% rename from Sources/BedrockAuthentication.swift rename to Sources/BedrockService/BedrockAuthentication.swift diff --git a/Sources/BedrockModel.swift b/Sources/BedrockService/BedrockModel.swift similarity index 99% rename from Sources/BedrockModel.swift rename to Sources/BedrockService/BedrockModel.swift index 4ddc0166..15bea094 100644 --- a/Sources/BedrockModel.swift +++ b/Sources/BedrockService/BedrockModel.swift @@ -37,6 +37,7 @@ public struct BedrockModel: Hashable, Sendable, Equatable, RawRepresentable { /// Creates a new BedrockModel instance /// - Parameters: /// - id: The unique identifier for the model + /// - name: The human-readable name of the model /// - modality: The modality of the model public init( id: String, diff --git a/Sources/BedrockService.swift b/Sources/BedrockService/BedrockService.swift similarity index 100% rename from Sources/BedrockService.swift rename to Sources/BedrockService/BedrockService.swift diff --git a/Sources/BedrockServiceError.swift b/Sources/BedrockService/BedrockServiceError.swift similarity index 100% rename from Sources/BedrockServiceError.swift rename to Sources/BedrockService/BedrockServiceError.swift diff --git a/Sources/Converse/BedrockService+Converse.swift b/Sources/BedrockService/Converse/BedrockService+Converse.swift similarity index 96% rename from Sources/Converse/BedrockService+Converse.swift rename to Sources/BedrockService/Converse/BedrockService+Converse.swift index 59ee8e97..7563fd96 100644 --- a/Sources/Converse/BedrockService+Converse.swift +++ b/Sources/BedrockService/Converse/BedrockService+Converse.swift @@ -34,6 +34,8 @@ extension BedrockService { /// - stopSequences: Optional array of sequences where generation should stop /// - systemPrompts: Optional array of system prompts to guide the conversation /// - tools: Optional array of tools the model can use + /// - enableReasoning: Optional flag to enable reasoning output + /// - maxReasoningTokens: Optional maximum number of reasoning tokens to generate /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt if the prompt is empty or too long @@ -83,6 +85,8 @@ extension BedrockService { /// - stopSequences: Optional array of sequences where generation should stop /// - systemPrompts: Optional array of system prompts to guide the conversation /// - tools: Optional array of tools the model can use + /// - enableReasoning: Optional flag to enable reasoning output + /// - maxReasoningTokens: Optional maximum number of reasoning tokens to generate /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt if the prompt is empty or too long diff --git a/Sources/Converse/BedrockService+ConverseStreaming.swift b/Sources/BedrockService/Converse/BedrockService+ConverseStreaming.swift similarity index 97% rename from Sources/Converse/BedrockService+ConverseStreaming.swift rename to Sources/BedrockService/Converse/BedrockService+ConverseStreaming.swift index 73df10a6..d475eec0 100644 --- a/Sources/Converse/BedrockService+ConverseStreaming.swift +++ b/Sources/BedrockService/Converse/BedrockService+ConverseStreaming.swift @@ -34,6 +34,8 @@ extension BedrockService { /// - stopSequences: Optional array of sequences where generation should stop /// - systemPrompts: Optional array of system prompts to guide the conversation /// - tools: Optional array of tools the model can use + /// - enableReasoning: Optional flag to enable reasoning output + /// - maxReasoningTokens: Optional maximum number of reasoning tokens to generate /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt if the prompt is empty or too long @@ -86,6 +88,8 @@ extension BedrockService { /// - stopSequences: Optional array of sequences where generation should stop /// - systemPrompts: Optional array of system prompts to guide the conversation /// - tools: Optional array of tools the model can use + /// - enableReasoning: Optional flag to enable reasoning capabilities + /// - maxReasoningTokens: Optional maximum number of tokens for reasoning /// - Throws: BedrockLibraryError.notSupported for parameters or functionalities that are not supported /// BedrockLibraryError.invalidParameter for invalid parameters /// BedrockLibraryError.invalidPrompt if the prompt is empty or too long diff --git a/Sources/Converse/ContentBlocks/Content.swift b/Sources/BedrockService/Converse/ContentBlocks/Content.swift similarity index 100% rename from Sources/Converse/ContentBlocks/Content.swift rename to Sources/BedrockService/Converse/ContentBlocks/Content.swift diff --git a/Sources/Converse/ContentBlocks/DocumentBlock.swift b/Sources/BedrockService/Converse/ContentBlocks/DocumentBlock.swift similarity index 100% rename from Sources/Converse/ContentBlocks/DocumentBlock.swift rename to Sources/BedrockService/Converse/ContentBlocks/DocumentBlock.swift diff --git a/Sources/Converse/ContentBlocks/DocumentToJSON.swift b/Sources/BedrockService/Converse/ContentBlocks/DocumentToJSON.swift similarity index 100% rename from Sources/Converse/ContentBlocks/DocumentToJSON.swift rename to Sources/BedrockService/Converse/ContentBlocks/DocumentToJSON.swift diff --git a/Sources/Converse/ContentBlocks/ImageBlock.swift b/Sources/BedrockService/Converse/ContentBlocks/ImageBlock.swift similarity index 100% rename from Sources/Converse/ContentBlocks/ImageBlock.swift rename to Sources/BedrockService/Converse/ContentBlocks/ImageBlock.swift diff --git a/Sources/Converse/ContentBlocks/JSONtoDocument.swift b/Sources/BedrockService/Converse/ContentBlocks/JSONtoDocument.swift similarity index 100% rename from Sources/Converse/ContentBlocks/JSONtoDocument.swift rename to Sources/BedrockService/Converse/ContentBlocks/JSONtoDocument.swift diff --git a/Sources/Converse/ContentBlocks/ReasoningBlock.swift b/Sources/BedrockService/Converse/ContentBlocks/ReasoningBlock.swift similarity index 100% rename from Sources/Converse/ContentBlocks/ReasoningBlock.swift rename to Sources/BedrockService/Converse/ContentBlocks/ReasoningBlock.swift diff --git a/Sources/Converse/ContentBlocks/S3Location.swift b/Sources/BedrockService/Converse/ContentBlocks/S3Location.swift similarity index 100% rename from Sources/Converse/ContentBlocks/S3Location.swift rename to Sources/BedrockService/Converse/ContentBlocks/S3Location.swift diff --git a/Sources/Converse/ContentBlocks/ToolResultBlock.swift b/Sources/BedrockService/Converse/ContentBlocks/ToolResultBlock.swift similarity index 100% rename from Sources/Converse/ContentBlocks/ToolResultBlock.swift rename to Sources/BedrockService/Converse/ContentBlocks/ToolResultBlock.swift diff --git a/Sources/Converse/ContentBlocks/ToolUseBlock.swift b/Sources/BedrockService/Converse/ContentBlocks/ToolUseBlock.swift similarity index 100% rename from Sources/Converse/ContentBlocks/ToolUseBlock.swift rename to Sources/BedrockService/Converse/ContentBlocks/ToolUseBlock.swift diff --git a/Sources/Converse/ContentBlocks/VideoBlock.swift b/Sources/BedrockService/Converse/ContentBlocks/VideoBlock.swift similarity index 100% rename from Sources/Converse/ContentBlocks/VideoBlock.swift rename to Sources/BedrockService/Converse/ContentBlocks/VideoBlock.swift diff --git a/Sources/Converse/ConverseReply.swift b/Sources/BedrockService/Converse/ConverseReply.swift similarity index 100% rename from Sources/Converse/ConverseReply.swift rename to Sources/BedrockService/Converse/ConverseReply.swift diff --git a/Sources/Converse/ConverseRequest.swift b/Sources/BedrockService/Converse/ConverseRequest.swift similarity index 100% rename from Sources/Converse/ConverseRequest.swift rename to Sources/BedrockService/Converse/ConverseRequest.swift diff --git a/Sources/Converse/ConverseRequestBuilder.swift b/Sources/BedrockService/Converse/ConverseRequestBuilder.swift similarity index 100% rename from Sources/Converse/ConverseRequestBuilder.swift rename to Sources/BedrockService/Converse/ConverseRequestBuilder.swift diff --git a/Sources/Converse/ConverseRequestStreaming.swift b/Sources/BedrockService/Converse/ConverseRequestStreaming.swift similarity index 100% rename from Sources/Converse/ConverseRequestStreaming.swift rename to Sources/BedrockService/Converse/ConverseRequestStreaming.swift diff --git a/Sources/Converse/ConverseResponseStreaming.swift b/Sources/BedrockService/Converse/ConverseResponseStreaming.swift similarity index 100% rename from Sources/Converse/ConverseResponseStreaming.swift rename to Sources/BedrockService/Converse/ConverseResponseStreaming.swift diff --git a/Sources/Converse/History.swift b/Sources/BedrockService/Converse/History.swift similarity index 100% rename from Sources/Converse/History.swift rename to Sources/BedrockService/Converse/History.swift diff --git a/Sources/Converse/JSON.swift b/Sources/BedrockService/Converse/JSON.swift similarity index 100% rename from Sources/Converse/JSON.swift rename to Sources/BedrockService/Converse/JSON.swift diff --git a/Sources/Converse/Message.swift b/Sources/BedrockService/Converse/Message.swift similarity index 100% rename from Sources/Converse/Message.swift rename to Sources/BedrockService/Converse/Message.swift diff --git a/Sources/Converse/Role.swift b/Sources/BedrockService/Converse/Role.swift similarity index 100% rename from Sources/Converse/Role.swift rename to Sources/BedrockService/Converse/Role.swift diff --git a/Sources/Converse/Streaming/ConverseReplyStream.swift b/Sources/BedrockService/Converse/Streaming/ConverseReplyStream.swift similarity index 100% rename from Sources/Converse/Streaming/ConverseReplyStream.swift rename to Sources/BedrockService/Converse/Streaming/ConverseReplyStream.swift diff --git a/Sources/Converse/Streaming/ConverseStreamElement.swift b/Sources/BedrockService/Converse/Streaming/ConverseStreamElement.swift similarity index 100% rename from Sources/Converse/Streaming/ConverseStreamElement.swift rename to Sources/BedrockService/Converse/Streaming/ConverseStreamElement.swift diff --git a/Sources/Converse/Streaming/ResponseMetaData.swift b/Sources/BedrockService/Converse/Streaming/ResponseMetaData.swift similarity index 100% rename from Sources/Converse/Streaming/ResponseMetaData.swift rename to Sources/BedrockService/Converse/Streaming/ResponseMetaData.swift diff --git a/Sources/Converse/Streaming/ToolUseStart.swift b/Sources/BedrockService/Converse/Streaming/ToolUseStart.swift similarity index 100% rename from Sources/Converse/Streaming/ToolUseStart.swift rename to Sources/BedrockService/Converse/Streaming/ToolUseStart.swift diff --git a/Sources/Converse/Tool.swift b/Sources/BedrockService/Converse/Tool.swift similarity index 100% rename from Sources/Converse/Tool.swift rename to Sources/BedrockService/Converse/Tool.swift diff --git a/Sources/BedrockService/Docs.docc/AddingModels.md b/Sources/BedrockService/Docs.docc/AddingModels.md new file mode 100644 index 00000000..d2d40cda --- /dev/null +++ b/Sources/BedrockService/Docs.docc/AddingModels.md @@ -0,0 +1,299 @@ +# Adding Models + +Extend BedrockService with new foundation models + +## Overview + +BedrockService is designed to be extensible. You can add support for new foundation models by implementing the appropriate modality protocols and creating BedrockModel instances. + +## Adding Converse-Only Models + +For models that only support the Converse API, use `StandardConverse`: + +```swift +extension BedrockModel { + public static let new_bedrock_model = BedrockModel( + id: "family.model-id-v1:0", + name: "New Model Name", + modality: StandardConverse( + parameters: ConverseParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.3), + maxTokens: Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: nil), + topP: Parameter(.topP, minValue: 0.01, maxValue: 0.99, defaultValue: 0.75), + stopSequences: StopSequenceParams(maxSequences: nil, defaultValue: []), + maxPromptSize: nil + ), + features: [.textGeneration, .systemPrompts, .document, .toolUse] + ) + ) +} +``` + +### Converse Features + +Specify which features the model supports: + +- `.textGeneration` - Basic text generation +- `.systemPrompts` - System message support +- `.vision` - Image input processing +- `.document` - Document input processing +- `.toolUse` - Function calling +- `.streaming` - Real-time response streaming +- `.reasoning` - Reasoning output + +## Adding Text Generation Models + +For models that need custom InvokeModel support, implement the required protocols: + +### Step 1: Create Request/Response Structures + +```swift +public struct LlamaRequestBody: BedrockBodyCodable { + let prompt: String + let max_gen_len: Int + let temperature: Double + let top_p: Double + + public init(prompt: String, maxTokens: Int = 512, temperature: Double = 0.5) { + self.prompt = "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\(prompt)<|eot_id|><|start_header_id|>assistant<|end_header_id|>" + self.max_gen_len = maxTokens + self.temperature = temperature + self.top_p = 0.9 + } +} + +struct LlamaResponseBody: ContainsTextCompletion { + let generation: String + let prompt_token_count: Int + let generation_token_count: Int + let stop_reason: String + + public func getTextCompletion() throws -> TextCompletion { + TextCompletion(generation) + } +} +``` + +### Step 2: Implement TextModality + +```swift +struct LlamaText: TextModality { + let parameters: TextGenerationParameters + + init(parameters: TextGenerationParameters) { + self.parameters = parameters + } + + func getName() -> String { "Llama Text Generation" } + + func getParameters() -> TextGenerationParameters { + parameters + } + + func getTextRequestBody( + prompt: String, + maxTokens: Int?, + temperature: Double?, + topP: Double?, + topK: Int?, + stopSequences: [String]? + ) throws -> BedrockBodyCodable { + guard topK == nil else { + throw BedrockLibraryError.notSupported("TopK is not supported for Llama") + } + guard stopSequences == nil else { + throw BedrockLibraryError.notSupported("Stop sequences not supported for Llama") + } + + return LlamaRequestBody( + prompt: prompt, + maxTokens: maxTokens ?? parameters.maxTokens.defaultValue, + temperature: temperature ?? parameters.temperature.defaultValue + ) + } + + func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { + let decoder = JSONDecoder() + return try decoder.decode(LlamaResponseBody.self, from: data) + } +} +``` + +### Step 3: Create BedrockModel Instance + +```swift +extension BedrockModel { + public static let llama3_3_70b_instruct: BedrockModel = BedrockModel( + id: "meta.llama3-3-70b-instruct-v1:0", + name: "Llama 3.3 70B Instruct", + modality: LlamaText( + parameters: TextGenerationParameters( + temperature: Parameter(.temperature, minValue: 0, maxValue: 1, defaultValue: 0.5), + maxTokens: Parameter(.maxTokens, minValue: 0, maxValue: 2_048, defaultValue: 512), + topP: Parameter(.topP, minValue: 0, maxValue: 1, defaultValue: 0.9), + topK: Parameter.notSupported(.topK), + stopSequences: StopSequenceParams.notSupported(), + maxPromptSize: nil + ) + ) + ) +} +``` + +## Adding Image Generation Models + +For image generation models, implement `ImageModality`: + +### Step 1: Create Request/Response Structures + +```swift +public struct AmazonImageRequestBody: BedrockBodyCodable { + let taskType: TaskType + private let textToImageParams: TextToImageParams? + private let imageGenerationConfig: ImageGenerationConfig + + public static func textToImage( + prompt: String, + negativeText: String?, + nrOfImages: Int?, + cfgScale: Double?, + seed: Int?, + quality: ImageQuality?, + resolution: ImageResolution? + ) -> Self { + // Implementation details... + } +} + +struct AmazonImageResponseBody: ContainsImageGeneration { + let images: [String] + + func getImageGenerationOutput() throws -> ImageGenerationOutput { + ImageGenerationOutput(images: images) + } +} +``` + +### Step 2: Implement ImageModality + +```swift +struct AmazonImage: ImageModality { + let parameters: ImageGenerationParameters + + func getName() -> String { "Amazon Image Generation" } + + func getImageGenerationParameters() -> ImageGenerationParameters { + parameters + } + + func hasTextToImageModality() -> Bool { true } + func hasImageVariationModality() -> Bool { false } + + func getTextToImageRequestBody(/* parameters */) throws -> BedrockBodyCodable { + // Implementation... + } + + func getImageResponseBody(from data: Data) throws -> ContainsImageGeneration { + let decoder = JSONDecoder() + return try decoder.decode(AmazonImageResponseBody.self, from: data) + } +} +``` + +## Hybrid Modalities + +For models supporting multiple capabilities, create custom modalities: + +```swift +struct ModelFamilyModality: TextModality, ConverseModality { + let parameters: TextGenerationParameters + let converseFeatures: [ConverseFeature] + let converseParameters: ConverseParameters + + init(parameters: TextGenerationParameters, features: [ConverseFeature] = [.textGeneration]) { + self.parameters = parameters + self.converseFeatures = features + self.converseParameters = ConverseParameters(textGenerationParameters: parameters) + } + + func getName() -> String { "Model Family Text and Converse" } + + // Implement TextModality methods + func getParameters() -> TextGenerationParameters { parameters } + func getTextRequestBody(/* ... */) throws -> BedrockBodyCodable { /* ... */ } + func getTextResponseBody(from data: Data) throws -> ContainsTextCompletion { /* ... */ } + + // Implement ConverseModality methods + func getConverseParameters() -> ConverseParameters { converseParameters } + func getConverseFeatures() -> [ConverseFeature] { converseFeatures } +} +``` + +## Parameter Validation + +Define parameter constraints carefully: + +```swift +// Supported parameter with range +Parameter(.temperature, minValue: 0.0, maxValue: 2.0, defaultValue: 1.0) + +// Supported parameter with no upper limit +Parameter(.maxTokens, minValue: 1, maxValue: nil, defaultValue: 1000) + +// Unsupported parameter +Parameter.notSupported(.topK) + +// Stop sequences with limits +StopSequenceParams(maxSequences: 4, defaultValue: []) + +// Stop sequences not supported +StopSequenceParams.notSupported() +``` + +## Testing New Models + +Test your model implementation: + +```swift +func testNewModel() async throws { + let bedrock = try await BedrockService() + let model = BedrockModel.new_bedrock_model + + // Test basic functionality + if model.hasTextModality() { + let completion = try await bedrock.completeText("Hello", with: model) + print("Text completion: \(completion.completion)") + } + + if model.hasConverseModality() { + let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Hello") + let reply = try await bedrock.converse(with: builder) + print("Converse reply: \(reply)") + } + + // Test parameter validation + do { + let _ = try await bedrock.completeText( + "Test", + with: model, + temperature: 5.0 // Should fail if max is < 5.0 + ) + } catch BedrockServiceError.parameterOutOfRange(let param, let value, let range) { + print("Expected parameter error: \(param) = \(value) not in \(range)") + } +} +``` + +## Best Practices + +1. **Follow AWS Documentation**: Check the official model documentation for exact request/response formats +2. **Validate Parameters**: Implement proper parameter validation based on model capabilities +3. **Handle Errors**: Provide clear error messages for unsupported features +4. **Test Thoroughly**: Test all supported features and parameter combinations +5. **Document Limitations**: Clearly document what features are and aren't supported + +## See Also + +- [AWS Bedrock Model Parameters](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters.html) +- [Converse API Supported Features](https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-supported-models-features.html) \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Authentication.md b/Sources/BedrockService/Docs.docc/Authentication.md new file mode 100644 index 00000000..716fd5a5 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Authentication.md @@ -0,0 +1,96 @@ +# Authentication + +Configure authentication for Amazon Bedrock access + +## Overview + +BedrockService supports multiple authentication methods to work with Amazon Bedrock. Choose the method that best fits your application's deployment environment and security requirements. + +## Default Authentication + +Uses the standard AWS credential provider chain: + +```swift +let bedrock = try await BedrockService( + region: .uswest2 + // authentication defaults to .default +) +``` + +The credential chain checks for credentials in this order: +1. Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) +2. AWS credentials file (`~/.aws/credentials`) +3. AWS config file (`~/.aws/config`) +4. IAM roles for Amazon EC2 instances +5. IAM roles for tasks (Amazon ECS) +6. IAM roles for Lambda functions + +## Profile-based Authentication + +Use a specific profile from your AWS credentials file: + +```swift +let bedrock = try await BedrockService( + region: .uswest2, + authentication: .profile(profileName: "my-profile") +) +``` + +## SSO Authentication + +Use AWS Single Sign-On authentication. Run `aws sso login --profile ` first: + +```swift +let bedrock = try await BedrockService( + region: .uswest2, + authentication: .sso(profileName: "my-sso-profile") +) +``` + +## Web Identity Token Authentication + +Use JWT tokens from external identity providers (ideal for iOS/macOS apps): + +```swift +let bedrock = try await BedrockService( + region: .uswest2, + authentication: .webIdentity( + token: jwtToken, + roleARN: "arn:aws:iam::123456789012:role/MyAppRole", + region: .uswest2, + notification: { + print("AWS credentials updated") + } + ) +) +``` + +## API Key Authentication + +Use API keys generated in the AWS console: + +```swift +let bedrock = try await BedrockService( + region: .uswest2, + authentication: .apiKey(key: "your-api-key-here") +) +``` + +> Important: Never hardcode API keys in your application. Use secure storage or environment variables. + +## Static Credentials (Testing Only) + +For testing and debugging purposes only: + +```swift +let bedrock = try await BedrockService( + region: .uswest2, + authentication: .static( + accessKey: "AKIAIOSFODNN7EXAMPLE", + secretKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", + sessionToken: "optional-session-token" + ) +) +``` + +> Warning: Never use static credentials in production or commit them to version control. \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/BedrockService.md b/Sources/BedrockService/Docs.docc/BedrockService.md new file mode 100644 index 00000000..7b37610c --- /dev/null +++ b/Sources/BedrockService/Docs.docc/BedrockService.md @@ -0,0 +1,40 @@ +# ``BedrockService`` + +@Metadata { + @PageKind(article) + @PageColor(green) + @SupportedLanguage(swift) + @PageImage(source: "bedrock.png", alt: "BedrockService", purpose: icon) +} + +A Swift library for interacting with Amazon Bedrock foundation models + +## Overview + +BedrockService is a lightweight layer on top of the AWS SDK for Swift that provides convenient access to Amazon Bedrock's capabilities. With support for text generation, image creation, embeddings, and conversational AI, this library makes it easy to integrate foundation models into your Swift applications. + +## Topics + +### Getting Started + +- +- + +### Core Features + +- +- +- +- + +### Advanced Topics + +- +- +- +- +- + +### Extending the Library + +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Converse.md b/Sources/BedrockService/Docs.docc/Converse.md new file mode 100644 index 00000000..a69f3c17 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Converse.md @@ -0,0 +1,92 @@ +# Converse API + +Build conversational AI applications with the Converse API + +## Overview + +The Converse API provides a unified interface for text-based interactions with foundation models. It supports multi-turn conversations, system prompts, and maintains conversation history automatically. + +## Basic Text Conversation + +Start a simple conversation with a foundation model: + +```swift +let model: BedrockModel = .nova_lite + +guard model.hasConverseModality() else { + throw MyError.incorrectModality("\(model.name) does not support converse") +} + +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("Tell me about rainbows") + +var reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") + +// Continue the conversation +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withPrompt("Do you think birds can see them too?") + +reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") +``` + +## Inference Parameters + +Control the model's behavior with inference parameters: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Tell me about rainbows") + .withMaxTokens(512) + .withTemperature(0.2) + .withStopSequences(["END", "STOP"]) + .withSystemPrompts(["Be concise", "Use simple language"]) + +let reply = try await bedrock.converse(with: builder) +``` + +## System Prompts + +Guide the model's behavior with system prompts: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withSystemPrompts([ + "You are a helpful assistant", + "Always provide accurate information", + "Be concise in your responses" + ]) + .withPrompt("What is machine learning?") +``` + +## Custom Messages + +Build messages manually for more control: + +```swift +// Simple text message +let reply = try await bedrock.converse( + with: model, + conversation: [Message("What day of the week is it?")] +) + +// With inference parameters +let reply = try await bedrock.converse( + with: model, + conversation: [Message("What day of the week is it?")], + maxTokens: 512, + temperature: 1, + topP: 0.8, + stopSequences: ["THE END"], + systemPrompts: ["Today is Wednesday, make sure to mention that."] +) +``` + +## See Also + +- +- +- +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Documents.md b/Sources/BedrockService/Docs.docc/Documents.md new file mode 100644 index 00000000..3d0ec28d --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Documents.md @@ -0,0 +1,139 @@ +# Documents + +Process documents with foundation models + +## Overview + +Document processing allows you to send PDF, text, and other document formats to foundation models for analysis, summarization, and question answering. + +## Basic Document Processing + +Send a document for analysis: + +```swift +let model: BedrockModel = .nova_lite + +guard model.hasConverseModality(.document) else { + throw MyError.incorrectModality("\(model.name) does not support documents") +} + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Can you give me a summary of this chapter?") + .withDocument(name: "Chapter 1", format: .pdf, source: base64EncodedDocument) + +let reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") +``` + +## Supported Document Formats + +BedrockService supports various document formats: +- PDF (`.pdf`) +- Plain text (`.txt`) +- Markdown (`.md`) +- CSV (`.csv`) +- Microsoft Word (`.docx`) + +## Document with Parameters + +Combine document processing with inference parameters: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Summarize the key points from this document") + .withDocument(name: "Report", format: .pdf, source: base64EncodedDocument) + .withMaxTokens(512) + .withTemperature(0.4) + +let reply = try await bedrock.converse(with: builder) +``` + +## Multi-turn Document Conversations + +Continue conversations about the same document: + +```swift +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("What are the main conclusions in this research paper?") + .withDocument(name: "Research Paper", format: .pdf, source: base64EncodedDocument) + +var reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") + +// Ask follow-up questions without re-sending the document +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withPrompt("What methodology did they use?") + +reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") +``` + +## Using DocumentBlock + +Create `DocumentBlock` objects for more control: + +```swift +let documentBlock = DocumentBlock( + name: "Financial Report Q4", + format: .pdf, + source: base64EncodedDocument +) + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Analyze the financial trends in this report") + .withDocument(documentBlock) +``` + +## Document Analysis Tasks + +Common document processing tasks: + +### Summarization +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Provide a concise summary of this document") + .withDocument(name: "Article", format: .pdf, source: documentData) +``` + +### Question Answering +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("What is the author's main argument about climate change?") + .withDocument(name: "Climate Paper", format: .pdf, source: documentData) +``` + +### Translation +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Translate this document to French") + .withDocument(name: "Contract", format: .pdf, source: documentData) +``` + +## Streaming with Documents + +Document processing works with streaming responses: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Analyze this legal document and highlight key clauses") + .withDocument(name: "Contract", format: .pdf, source: base64EncodedDocument) + +let stream = try await bedrock.converseStream(with: builder) + +for try await element in stream { + switch element { + case .text(_, let text): + print(text, terminator: "") + case .messageComplete(_): + print("\n") + default: + break + } +} +``` + +## See Also + +- +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Embeddings.md b/Sources/BedrockService/Docs.docc/Embeddings.md new file mode 100644 index 00000000..73f92c47 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Embeddings.md @@ -0,0 +1,274 @@ +# Embeddings + +Generate vector embeddings for semantic analysis + +## Overview + +Embeddings convert text into numerical vector representations that capture semantic meaning. These vectors enable similarity comparisons, clustering, and retrieval-augmented generation (RAG) systems. + +## Basic Embeddings + +Generate embeddings from text: + +```swift +let model: BedrockModel = .titan_embed_text_v2 + +guard model.hasEmbeddingsModality() else { + throw MyError.incorrectModality("\(model.name) does not support embeddings") +} + +let embeddings = try await bedrock.embed( + "Swift is a powerful programming language", + with: model +) + +print("Generated embeddings with \(embeddings.count) dimensions") +print("First few values: \(Array(embeddings.prefix(5)))") +``` + +## Vector Size Control + +Specify the embedding vector size: + +```swift +let embeddings = try await bedrock.embed( + "Machine learning and artificial intelligence", + with: model, + vectorSize: 512 +) + +print("Embedding dimensions: \(embeddings.count)") +``` + +## Semantic Similarity + +Compare text similarity using embeddings: + +```swift +let text1 = "The cat sat on the mat" +let text2 = "A feline rested on the rug" +let text3 = "Quantum computing uses qubits" + +let embedding1 = try await bedrock.embed(text1, with: model) +let embedding2 = try await bedrock.embed(text2, with: model) +let embedding3 = try await bedrock.embed(text3, with: model) + +// Calculate cosine similarity +func cosineSimilarity(_ a: [Double], _ b: [Double]) -> Double { + let dotProduct = zip(a, b).map(*).reduce(0, +) + let magnitudeA = sqrt(a.map { $0 * $0 }.reduce(0, +)) + let magnitudeB = sqrt(b.map { $0 * $0 }.reduce(0, +)) + return dotProduct / (magnitudeA * magnitudeB) +} + +let similarity12 = cosineSimilarity(embedding1, embedding2) +let similarity13 = cosineSimilarity(embedding1, embedding3) + +print("Similarity between text1 and text2: \(similarity12)") +print("Similarity between text1 and text3: \(similarity13)") +// text1 and text2 should have higher similarity than text1 and text3 +``` + +## Batch Processing + +Process multiple texts efficiently: + +```swift +let texts = [ + "Apple is a technology company", + "Bananas are yellow fruits", + "Microsoft develops software", + "Oranges are citrus fruits", + "Google creates search engines" +] + +var embeddings: [[Double]] = [] + +for text in texts { + let embedding = try await bedrock.embed(text, with: model) + embeddings.append(embedding) +} + +// Find most similar texts +func findMostSimilar(to queryIndex: Int, in embeddings: [[Double]]) -> Int { + var maxSimilarity = -1.0 + var mostSimilarIndex = 0 + + for (index, embedding) in embeddings.enumerated() { + guard index != queryIndex else { continue } + + let similarity = cosineSimilarity(embeddings[queryIndex], embedding) + if similarity > maxSimilarity { + maxSimilarity = similarity + mostSimilarIndex = index + } + } + + return mostSimilarIndex +} + +let queryIndex = 0 // "Apple is a technology company" +let similarIndex = findMostSimilar(to: queryIndex, in: embeddings) +print("Most similar to '\(texts[queryIndex])': '\(texts[similarIndex])'") +``` + +## Document Retrieval System + +Build a simple RAG system: + +```swift +struct Document { + let id: String + let content: String + let embedding: [Double] +} + +class DocumentStore { + private var documents: [Document] = [] + private let bedrock: BedrockService + private let model: BedrockModel + + init(bedrock: BedrockService, model: BedrockModel) { + self.bedrock = bedrock + self.model = model + } + + func addDocument(_ content: String, id: String) async throws { + let embedding = try await bedrock.embed(content, with: model) + let document = Document(id: id, content: content, embedding: embedding) + documents.append(document) + } + + func search(_ query: String, topK: Int = 3) async throws -> [Document] { + let queryEmbedding = try await bedrock.embed(query, with: model) + + let similarities = documents.map { doc in + (doc, cosineSimilarity(queryEmbedding, doc.embedding)) + } + + return similarities + .sorted { $0.1 > $1.1 } + .prefix(topK) + .map { $0.0 } + } +} + +// Usage +let store = DocumentStore(bedrock: bedrock, model: model) + +try await store.addDocument("Swift is a programming language developed by Apple", id: "doc1") +try await store.addDocument("Python is popular for data science and machine learning", id: "doc2") +try await store.addDocument("JavaScript runs in web browsers and Node.js", id: "doc3") + +let results = try await store.search("Apple programming language") +for doc in results { + print("Found: \(doc.content)") +} +``` + +## Text Clustering + +Group similar texts using embeddings: + +```swift +struct TextCluster { + let centroid: [Double] + var texts: [String] + var embeddings: [[Double]] +} + +func kMeansClustering(texts: [String], embeddings: [[Double]], k: Int, iterations: Int = 10) -> [TextCluster] { + // Simple k-means implementation + var clusters = Array(0.. +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/ImageGeneration.md b/Sources/BedrockService/Docs.docc/ImageGeneration.md new file mode 100644 index 00000000..93a6a258 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/ImageGeneration.md @@ -0,0 +1,190 @@ +# Image Generation + +Create and modify images with foundation models + +## Overview + +BedrockService supports image generation and variation capabilities, allowing you to create images from text descriptions and generate variations of existing images. + +## Text-to-Image Generation + +Generate images from text descriptions: + +```swift +let model: BedrockModel = .nova_canvas + +guard model.hasImageModality(), + model.hasTextToImageModality() else { + throw MyError.incorrectModality("\(model.name) does not support image generation") +} + +let imageGeneration = try await bedrock.generateImage( + "A serene landscape with mountains at sunset", + with: model +) + +// Access generated images +for (index, image) in imageGeneration.images.enumerated() { + print("Generated image \(index + 1): \(image.prefix(50))...") +} +``` + +## Generation Parameters + +Control image generation with various parameters: + +```swift +let imageGeneration = try await bedrock.generateImage( + "A futuristic city skyline at night", + with: model, + negativePrompt: "dark, gloomy, abandoned", + nrOfImages: 3, + cfgScale: 7.0, + seed: 42, + quality: .standard, + resolution: ImageResolution(width: 1024, height: 1024) +) +``` + +### Available Parameters + +- **negativePrompt**: Describe what to avoid in the image +- **nrOfImages**: Number of images to generate (1-4 typically) +- **cfgScale**: How closely to follow the prompt (1.0-20.0) +- **seed**: For reproducible results +- **quality**: Image quality setting (`.standard`, `.premium`) +- **resolution**: Output image dimensions + +## Image Variations + +Create variations of existing images: + +```swift +let model: BedrockModel = .nova_canvas + +guard model.hasImageModality(), + model.hasImageVariationModality() else { + throw MyError.incorrectModality("\(model.name) does not support image variations") +} + +let imageVariations = try await bedrock.generateImageVariation( + images: [base64EncodedImage], + prompt: "A dog drinking from this teacup", + with: model +) +``` + +## Variation Parameters + +Fine-tune image variations: + +```swift +let imageVariations = try await bedrock.generateImageVariation( + images: [base64EncodedImage], + prompt: "Transform this into a watercolor painting", + with: model, + negativePrompt: "photorealistic, sharp edges", + similarity: 0.8, + nrOfVariations: 4, + cfgScale: 7.0, + seed: 123, + quality: .premium, + resolution: ImageResolution(width: 512, height: 512) +) +``` + +### Variation-Specific Parameters + +- **similarity**: How similar variations should be to source (0.0-1.0) +- **nrOfVariations**: Number of variations to create + +## Working with Image Data + +Handle base64-encoded image data: + +```swift +// Convert image file to base64 +func loadImageAsBase64(from path: String) -> String? { + guard let imageData = FileManager.default.contents(atPath: path) else { + return nil + } + return imageData.base64EncodedString() +} + +// Save generated image +func saveBase64Image(_ base64String: String, to path: String) { + guard let imageData = Data(base64Encoded: base64String) else { + print("Invalid base64 data") + return + } + + do { + try imageData.write(to: URL(fileURLWithPath: path)) + print("Image saved to \(path)") + } catch { + print("Failed to save image: \(error)") + } +} + +// Usage +if let sourceImage = loadImageAsBase64(from: "input.jpg") { + let variations = try await bedrock.generateImageVariation( + images: [sourceImage], + prompt: "Make this image look like a vintage photograph", + with: model + ) + + for (index, image) in variations.images.enumerated() { + saveBase64Image(image, to: "variation_\(index).jpg") + } +} +``` + +## Model-Specific Capabilities + +Different models support different features: + +```swift +// Check model capabilities +let model: BedrockModel = .nova_canvas + +if model.hasTextToImageModality() { + print("Supports text-to-image generation") +} + +if model.hasImageVariationModality() { + print("Supports image variations") +} + +// Get model-specific parameter limits +if let imageModality = model.modality as? ImageModality { + let params = imageModality.getImageGenerationParameters() + print("Max images: \(params.nrOfImages.maxValue ?? "unlimited")") + print("CFG scale range: \(params.cfgScale.minValue)-\(params.cfgScale.maxValue ?? 20)") +} +``` + +## Error Handling + +Handle common image generation errors: + +```swift +do { + let images = try await bedrock.generateImage( + "A beautiful sunset over the ocean", + with: model, + nrOfImages: 5 // Might exceed model limit + ) +} catch BedrockServiceError.parameterOutOfRange(let parameter, let value, let range) { + print("Parameter \(parameter) value \(value) is outside allowed range: \(range)") +} catch BedrockServiceError.notSupported(let feature) { + print("Feature not supported: \(feature)") +} catch { + print("Image generation failed: \(error)") +} +``` + +## See Also + +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/QuickStart.md b/Sources/BedrockService/Docs.docc/QuickStart.md new file mode 100644 index 00000000..39fe2d78 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/QuickStart.md @@ -0,0 +1,65 @@ +# Quick Start + +Get up and running with BedrockService in minutes + +## Overview + +This guide will help you quickly set up and start using BedrockService in your Swift project. + +## Installation + +Add BedrockService to your Swift package: + +```bash +swift package add-dependency https://github.com/build-on-aws/swift-bedrock-library.git --branch main +swift package add-target-dependency BedrockService TargetName --package swift-bedrock-library +``` + +Update your `Package.swift` to include platform requirements: + +```swift +import PackageDescription + +let package = Package( + name: "ProjectName", + platforms: [.macOS(.v15), .iOS(.v18), .tvOS(.v18)], + dependencies: [ + .package(url: "https://github.com/build-on-aws/swift-bedrock-library.git", branch: "main"), + ], + targets: [ + .executableTarget( + name: "TargetName", + dependencies: [ + .product(name: "BedrockService", package: "swift-bedrock-library"), + ] + ) + ] +) +``` + +## Basic Usage + +```swift +import BedrockService + +// Initialize the service +let bedrock = try await BedrockService(region: .uswest2) + +// List available models +let models = try await bedrock.listModels() + +// Send a simple text prompt +let model: BedrockModel = .nova_lite +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Tell me about rainbows") + +let reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") +``` + +## Next Steps + +- Learn about different methods +- Explore for conversational AI +- Try for creating images +- Check out for function calling \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Reasoning.md b/Sources/BedrockService/Docs.docc/Reasoning.md new file mode 100644 index 00000000..df391bc6 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Reasoning.md @@ -0,0 +1,186 @@ +# Reasoning + +Access the model's reasoning process + +## Overview + +Reasoning capabilities allow you to see how foundation models think through problems, providing transparency into their decision-making process. + +## Basic Reasoning + +Enable reasoning to see the model's thought process: + +```swift +let model: BedrockModel = .claudev3_7_sonnet + +guard model.hasConverseModality(.reasoning) else { + throw MyError.incorrectModality("\(model.name) does not support reasoning") +} + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Solve this math problem: If a train travels 60 mph for 2.5 hours, how far does it go?") + .withReasoning() + +let reply = try await bedrock.converse(with: builder) + +if let reasoning = try? reply.getReasoningBlock() { + print("Reasoning: \(reasoning.reasoning)") +} +print("Answer: \(reply)") +``` + +## Reasoning with Token Limits + +Control the length of reasoning output: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Explain the causes of World War I") + .withReasoning(maxReasoningTokens: 1024) + +let reply = try await bedrock.converse(with: builder) +``` + +## Streaming Reasoning + +See reasoning unfold in real-time: + +```swift +let builder = try ConverseRequestBuilder(with model) + .withPrompt("Plan a 7-day trip to Japan") + .withReasoning(maxReasoningTokens: 2048) + +let stream = try await bedrock.converseStream(with: builder) + +var reasoningIndexes: [Int] = [] +var textIndexes: [Int] = [] + +for try await element in stream { + switch element { + case .reasoning(let index, let reasoning): + if !reasoningIndexes.contains(index) { + reasoningIndexes.append(index) + print("\n🤔 Reasoning: ") + } + print(reasoning, terminator: "") + + case .text(let index, let text): + if !textIndexes.contains(index) { + textIndexes.append(index) + print("\n💬 Response: ") + } + print(text, terminator: "") + + case .messageComplete(_): + print("\n") + + default: + break + } +} +``` + +## Complex Problem Solving + +Use reasoning for multi-step problems: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt(""" + A company has 150 employees. 60% work in engineering, 25% in sales, + and the rest in administration. If engineering gets a 10% budget increase + and sales gets a 5% increase, what's the total percentage increase + in employee-related costs? + """) + .withReasoning() + .withTemperature(0.1) // Lower temperature for more focused reasoning + +let reply = try await bedrock.converse(with: builder) + +if let reasoning = try? reply.getReasoningBlock() { + print("Step-by-step reasoning:") + print(reasoning.reasoning) + print("\nFinal answer:") +} +print(reply) +``` + +## Reasoning in Conversations + +Maintain reasoning across conversation turns: + +```swift +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("I need to choose between two job offers. Can you help me think through this?") + .withReasoning() + +var reply = try await bedrock.converse(with: builder) + +if let reasoning = try? reply.getReasoningBlock() { + print("Initial reasoning: \(reasoning.reasoning)") +} +print("Assistant: \(reply)") + +// Continue with more details +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withPrompt(""" + Job A: $80k salary, great benefits, 30-minute commute, startup environment + Job B: $75k salary, okay benefits, 10-minute commute, established company + """) + .withReasoning() + +reply = try await bedrock.converse(with: builder) + +if let reasoning = try? reply.getReasoningBlock() { + print("Analysis reasoning: \(reasoning.reasoning)") +} +print("Assistant: \(reply)") +``` + +## Reasoning with Tools + +Combine reasoning with function calling: + +```swift +let calculatorTool = try Tool( + name: "calculate", + inputSchema: JSON([ + "type": "object", + "properties": [ + "expression": ["type": "string"] + ], + "required": ["expression"] + ]), + description: "Perform mathematical calculations" +) + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Calculate the compound interest on $1000 at 5% annually for 3 years") + .withTool(calculatorTool) + .withReasoning() + +let reply = try await bedrock.converse(with: builder) + +// The model will reason about the problem and potentially use the calculator tool +if let reasoning = try? reply.getReasoningBlock() { + print("Reasoning: \(reasoning.reasoning)") +} + +if let toolUse = try? reply.getToolUse() { + let expression: String? = toolUse.input["expression"] + let result = calculate(expression ?? "") + + let finalBuilder = try ConverseRequestBuilder(from: builder, with: reply) + .withToolResult(result) + .withReasoning() + + let finalReply = try await bedrock.converse(with: finalBuilder) + print("Final answer: \(finalReply)") +} +``` + +## See Also + +- +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Resources/bedrock.png b/Sources/BedrockService/Docs.docc/Resources/bedrock.png new file mode 100644 index 0000000000000000000000000000000000000000..38050a7af63d85fa8be4b9bc548e4589c512e0c0 GIT binary patch literal 191481 zcmeEuXIK+!*RCKaMMZG~7Mc~Mib^j*R8XV{Bp_WyKuSPbXaNMoh9oM|OH@Q4p<_ZX z%7%alNC_>3Vj$Ge0tqc}2GISw-}8Mx&X4oYAGxl{FqwJQdRDpDz1HyTroQ%Gt|MGK zcI?=D{n{0y9XocZ03QX8UBDTo(5CzyJ6KLTUcP+O`Lgz9Hy1Y#;|F(a?JnJOyK85p zttqRdq_|_pnOC;AZyQ~cJX3#%_xA1jCW*6L9zI4-pT01<9o$e|E6Vk^`5wz>4YlkD z78dnA8aq-AKNmUgc_F$|AYCu1>sS6z;uKc#@U;z=A5+7j?|FCas@o%KQBwY4$KTT| z1y^4ee4b(#g&p`FqCw~1uOjqCoyBD5odFgMC#T1*0?>{g`1RZJ2kWIPy*Mm({QbC> zn>7@1cKLcHm*{}=A#-!{`>)4#hrT_Deb079WmEvnZ}-vR%^5}3%i@sF7i7!~v_IEZ z*S`s2`D=PV0k!k!N?_`X4^TKX&fitijc%nTOAx z;~2YeZYnX#(j2@??(wUJxR8+jB0I!?=vc`|oUaEIr)YQUy1ky>4sqa^V+YF<#~p0I z5ex7+0(^GtV13K_>&z~dw>$qiR@nTb*n^%AJ9cR7xPIjl*q3FB)DvT63Oh~V3mizb z+Z}q(=oI8D`i-hiD){W`d#@qw+2n!GJU@J9lTn(&!rr#ARKt-hFy-1S-gsS{=))cF zL9L%R`a<5;58Ll%y**W36u(<6FV$kyV)V+c&Z^M_TcpP)!HxC}dR0eq*bWv}Hje$G z8V~>b;M}2*jm6+K6mjXcmh;>H`5NF?A;K(ow0WKz|8EymKVW+#R1+_8;?)2Bi0w1} zzy)e&^bhc~9*3M4Z~l9Qziv1NT(Iy~@#_C2!p{r#Xlr!KCY78w`d=i~ zcne(68usI_|5X)&EO+n^C||E}{!fYmp7KWtf0VH8M1L&dk0t!Egnt6*A9wiU4u9O? zk2?Tf@J9)Ml<;4L#-Hr)Cp-Mf4u1mUpTPJhF#h8O{~Y|u4u7)4pX~6*9sXp8KiT0Q zFZk!+Pj>i|9sXp8Kko1+JN$ph4yOZ}mHug`p1U2AAG2|~j@aFOEAh}#w}a$^B;vAj zz`~g#{Du%JX?OeJ_Mc`4rXOZgL6Vd_P@CNCaC!BApaXw_@|)lv6$Vudb~x3(zf=sXiI- zbi3hPeTwf|hAjOIL=f`%m8dNoUf!gy>rD_W^yG#Xd_j!&$j4TvenFHbgS%hdPSr%l zg98PHL~teWNG^?i21uf#I7oa)<{0_nD0==x_;JX-AOF>c7BJE1cE6KeSN%)P1JXB% zkW=IL$(+irHo@5JW*ID~YQ@RI;CucnqV5+2muB5Trw)FTQSG8=NKrDuQAKAgKS0rm zM|+hjGBe<~ich6CR~Qi2hb{%o&IYNB=#H~-#cbOo8a$60`CL@<~QqijRVjpR8AA2cdZ>d5;lpuvW zc&|i7<*zVPJoL?TBzjiyD{*nosuu5;`Q$lr<~Tj*-}>Fh`UNriaLVFWr#t1N2GqIeX(w!VlJ0xghQL7EObxd!HMx%&@8mL4mvI$*c>5a09= z!E@8%-EsQ0^`9FI(N|4)VJ~{=twCSjTvP_BJEk_&;MLU88+gYq?%_{pbq^Jpm8BmM ze*ZNGp=t|s{4eaHkNoQRgG}CvD^V!CPA9)F&A2ihEpA-JTTCjHrU*h_8Q!T~Vq^_3 zRoZ5jzFg_0K}(s7krq~a@m0a&kvTtX#XjTD9IKz14(!CW5y|b;CVE4&J&{CoEGtu4 z{-#X|9$*A4EBzKb#!zh1*1*N?n+_l|0~R%Xt^p-T_}7dy6G6(66D8=xN34SnaFA zV(H6;%J0RbTl>i`T2cS9z1aoe(FOw@rYC=T^V*pZ>EU{QBZ={vIr^eS585f8xi-<* zqv=#?6>oDtswW5S3L?m=D8=XN;!rkznZz$nZ}KDCh_;}nCWi6i-1xM~s+R+XN}+;O z%ZXZ<>)ky))0+DO)q~Jxw7_2~-dx0k65!df0YPfY+v>Qj!Ac%)FeDo4y+9VlkC))y zP|-QLS=ggA2@(zxrL^&>2;(Zp-G{@o1j}(n)jw(&=@LCyqX-?*S^+lz;K3yEpXj)m2vx&$lYj_H`k?L z37Zhwr<9@-WLJo+h?o8 z4=Bzw8nS%85*j+v6OMaTP4;I+D0Jo)m4LM+b@goG=7PF3n$h}orLw_(ePZK7`H5c< zPg29=b?z3%7KsQ>7cpxZU>=H2c+SiE9x5|)?wzE|wRj)D0MI|cVe?=u0`NH-$?{*W z=$pgpSxAFQLj*y@;@1BD;`kszAkK&%|Ja^u>kY%1zX5g4sbbtrh}MuL*(f~r+@iTk zLnWjNFtFY>CkZ|g$oSkeC3(8iAMPT?ktr;q&3P3JSH+mkdH;L20fA$k)zyytv)c!I z5}Zz3K&;Fxp}D!F`_6@)P&Jl%J5}-7xVURFui1o2uPBp{hJ4Ssj}%T7oB@(X&mgJv zIX|I(qDE3Xnynq4qzEIA_5Ev_s9J%OL#6V1o!?I%t2X{CLp<`CcA$&nP1)B6vPnCLaziORGU*4q9)=u}Dp?6)lOkndg)&rJ1)zi7jgyL>>dJiH$8V((lv zv{ViZ*L!A0<%aU#LJs%WfBETl$iDMr^p{-^9_ILP>lpVu8LHZZ&&|%lhR2t{R4gFb zA417=e{q$01~$I2#E#HDn50ufM=up)3;J_JM2Zp}d)LLByLzodk)>*i-IK1V3 zCMw@dZtWi5m^;JN=zyz69-fu^l@mAZ^_(OSGV>^n_qTUA0lR&q+Mu31(y z;!5lEXcTtR>{D)bqVx^goP(T@-5c{bn6o2OV&FaCxb*c-CVjmoz$%Ou6(CYXYIQvt zsl}5uNv22^35g#8xm# zF#m-8_au)!6&x;5#6}fL;X?C`Yn|IDg z%CG&~Y?{Uz0Qh5>(sAPW&-|ELeQQPMg+pUn!=uBKo)Hewf{wU~q#!}hC}XSoJB?@u z&@nr>^WQ3 zx%2mkkn#=)2#0^HwEYZ3BLZ>tF*-C_1XOk}eq*|+>e^ub_x^7<2$s<1)W{4i zsy%ZdqhAm)Ds}U7n*cpNj20T1IoANQy^}^2NlPFv6jNXD_fr1WRZ$hTTg4cv=%nGt z6q{O%sqRTZB4W#NGV05IAdUg#?>c7-$^+!z9ktB1z2Spb`7_?y!aWLrj9U~;n?QY< zjF6yKRk&mH9FAhfT`V9G)PtGlA6V`QNqB~)XAos_D%$o=47X;x*esr5s_yH(bR=$r zzV1N1;~xkw_pS;osOW*Ub;AREBt$H%)SJn#Bj$Wxg#K#{D}W-H(#^a-^Z)u)Q3*HIZtxR z=cEbX7hNEW16A`dad!LOwr?&)F4j#ALNy07cFlW2(ag24O*uiS3DKst_)7vFf><7- z@wrP7{|^(Jc0N+M0`I_&(`3t++ z^rS)W1Z6VUdNCxZt?N5t0UC!%NK9CHXVZ-5xpv$CIMdW4zo>W%WIPpG5-)F39JrQW8 zUr!5MTZCBq_J+ATS=1VkLk^hj)X~=E*f;fASI6K(~jp~opW*b zS7w@j?DfZ8#M~su=9c%!of}Z^n>muOL75h_;+XHJI6$30bqTww-G9=j&{w&)Y?_S z3R&hOR>-2^JAB`;Supa54xbBH6!yU5&1j4D7la{Ez1M)R{$8T0K2{rjso5W%gd>Cf zlaf-r^qMiP%YLnRgQb^n=ie7%$ptJVEl!K)r|T^pVF#OY+@$5{(DUZ6ptu>9;aPCvPi(_db4uPN@K{@_efD>P3`hWC}P&oRBGwZ{kY zm8;t8=KFf}jA>jNy>o^tdMyCv&$#)46&cmO7N4DO{)2Z8Uz=$AE1zuc``}$}pxC6Z z9TM91jM=}~Pq(?Oe4QWwcsp~QGE(Frx9(C_S=>fx!`OODca4lPLlNm5(>G47#ZBP~ zSC*q=OQo{3Ol=%f*@RX(RVU~GTJqW;tS4kc(TQtp$j9?B}o_D_hfMlYvnLQ?fN*{SW6ggD6L!RXq z_6tk2d)h3|L#T#D@$n_{F{2~&`&F2wZUaS+*UynrQBjXhh%F~+mjqkR$;LeAIVED_ zz98`X?k53KH=pYJSz*QM-@XekXlj=Bio|bF8$r$zobvtG>Sbdex0+G}+VA_TRw%Iv z#k&63J^N7nHm34iQLI95Q}~E<_tIoy5z&zKB{i%9m&}D^7>&EX6n{D37SzAbXgMEC z%j3QThVQu@y7$$80({7*wCI%Y^l;^|pDLGRFYV_XZ;(|_sT3@^TZzju|4N|cW?5ag zL4?y+zVby2ws^3@sWYc;2HK*h?;o?sAW+v7uk#>DV3?|)&?iInd%6Akv-!b_1O$&Z zRq;;!b=Off_2$xmj3q^er@?=;UL7F;JlcJ<|HO8Vjn#j9efya?2H&=Z8C z0HfjIe;RxWuW4HQ?lrkhZ#B(d0|EsP;dN8++{U@ss40uZh=t`0scf9loME)^jBEe+ zGzO2S4+V9R$!Fd@eU7}b2%f$DE35#~Zz%+z7DPBGH9x(cTk>U|t)#_H$W^@75#&HU zwP|tE>J+{wdO^T<>ao^fz6QJS!EssR5N)56Uy%T_?1@6#!x2; zwzs~jZ}-rTef{+u@Zb>iLR|cPYSTjUI6`%FGDBD-bzyF4k%T0MJrWdgB{@@vDqP&K z1TDV-v$kW?V|i~3&J`i`(jzY4sG(83qwvpsmDy}Z$0V_nJ!U%{WSkfadp@1tfEOgo|+b(hQq=t zsHc~@Z$#t67FIHdXJ*QZstEfE`8q#;4hnb^B<&D7)nn0~?B9nYRz zFe7LJbtCj%DZ_qRk49!YAxkT&ysIiuI>>A`K{1B9K1TsjE7kA|q8(oT9GUxenfo{G zhXLx?1Dn{BwB=~!hgb=i+sBCv-49$t1gx`J9Lljvz?8U%hY0a`b%a)=+PpntE;3AS zpxdu19>bH8pOC0o1-9aWq91#m#91gt2MU#|Iu^r=^YeP;ylsG}zK*!V1x}^)u1u`- z86{p{#Gh#iixr6V2=2zgV7=DsL1NJW;<%!2P=!p5O2rjb5%moYhQ2t(_9|?T=a0yQ ze*!=?w>V&;m&E#OPH*A*umknxtA7uBCF<1!9E&NDz$R2=D&I>TiTtek2<7pX59VUe zof>=%^kjn(#prFGHX$}s*W$9iA{La*^urj$qcYwWA$kY{T(}D#?wlOq^hF;~`jI~7 zJ)C%=3z_QwC95>~|E2N>dB?$_^7y=Q-S!S!{}uA+s4ddS;GB9V#eq`#HB5q2k)K8< zfQ7RtLGlC+L{Nx%eNOm9lZ%;GoCJO5ba!!C5owyL?(zhik?d)sj&`eY_g4}e4&+%UQXFGP_SZ*bbFp zs_)}{m=&o9;}a%o`{n$qWBd?;*mhSUM%9E*q$c4(>aQ|;zvyfzAc6JEXY_Mh0jK&V zTj|vWHX$sOaJ0+X`RI7l+>DK1q1BD=vWeL-PR=P#byH&kiq{?(5(GEqYJ7SfdJ@J8 zS5a64g_(PwS1v|$iCGy>$OKaw#@&fG4i!`twabh2Q#D`M1kMuWQ*PSI63v=UFZq%u znl1*R9D@_SA_?)!-)c3B)Y@XJ5JgW;+{{IVm&Z^%iE&nA1rfoFwn^&A&t1)}mr_7a zsHUjkHg=(z{*8+j>M&y1ZU*tpXMCzj;J<-mu`duqBNW86wzH`9wUB(t>gErjx5cB` zOnF_teJB_t5F&9gMX~9aSFrxr%AI!+xgfO(&DCs|>)@sg4C9My!pmU#Y*1P3Iyd&F zmFX19>cxQP^&ou$E387y?3$0@NaYuVcF#Jxl0wS9c(Dck4Ik|3kAqTWV4sb{4Itx& z(ei~o`!M}E=f2AB3GtUABzbE$G01le?2Z;5<(7bMoFNEu*1)v5p`Ld?3JD_%n1l?W zk!R<1GECGo5VW<7>Zu5Wpf$OFaWsQ|Kr&7#DxUcn#uvDBPY)L->>75aMNor_nFF8F zB+J`9ydDd$y~PZikD|fQHA=r2wY}mA6%;(HPwZ=efyb+E6895UQ6Dxmp=Mx$ z@NUj`L1l$K5ct|k7?|*3;-f$O+lMxC=Srk&Kz>&nXaqa5h&z9QXmZ|d)3=ul3Bum1 zwOG^A-R|>aCJaIJ4yx$hZ^4N=@1AgWs`r-;$W;tnFNSsQq%@>B)Vw+G^c$j(eh(BA z$D5zaZm~}x0vZ5kT-UyO=RARR($gWDH8(!>#T9`fhV_2 z2T=?oz+n<4rg(@?dO|0xzdWOYJ;ABt?UwE~OvRL?`g9ndNXf?gA17?GC{)9hkQWGp z-U{jysGHaKIvNYUV2Kx0*U0=MltjBIz($w5s)bj|^Wjx{@ z=qME}>}iEUPVg88s$Z1bK##m!myf+|o1=$C*_8B9Z$Y9#5IL{!;ey(J>iupaE^hXX z7*(0JQGnwhjHasVqmaj%KIQ+ap*i*+%6XV)TkPpDyj6|ezQuZ7+9|)xUs+dA4@MNt zrijngx&(p=J0L7KxzSj=X!0cMDAABAEDQ_0C_FXuX?Nz2?gqLhw)Xp-9CYqFXA60W z{2WP}@k=OCX;?E^Iv)$oG3PH;XkS}9=hl^Ff~~$Mu8IbF2O#lpZDg{g+abn50F?;? zReD;>6-waQ+@a_iLyBKHrX%6T0CKJ-H3x;i!l zGyIPnqO;`yb=q>Hm$6Mh$rM-L;{S{{&d*ma>R4YJDeA@6j?iPfY$nl)l=irC@ttf! zOoP`>99Dt6umVWOK-C73F3Kk)(!YEzT2Q;ESsd_LKBMN=jfR%FsXLmnsV^KrEO7!Ko&xGHh40et4E{(|pKQI3Yg2aAMuP4sFru`hpdhnvm+DkdIw{Lw z|2~r0HMtfq0WWa9T=eKA2xkYs{UZG4T&o+rwK>I24<`Qvi77aryV#c;pX6#B-?BPB zXY{Twtkv}kf-O;7+lsI444=qil9JA>yAkm&q*lA=x{2_J0g7aG(TV(v8{Dk1RDcZ| zU>PR;q|~A~z2<6D^Kto~`g3xu9Ze6quFXfI>_P%QN{KCGyu92#5kxefRZ~@R^f~CX zHh?N0m`)SH$5TKttmArsb?PlkNCiHK5-NhV#qU{H&%$u6_Xn6s%1CP5vsd3n9| zg^ED|Wc#8;0&%jd_v?aX*=tc3Eo0g{-MOwhGlFtNdpjrYBLK)W1)P9KE0m2+!%}Jp z-`5m;K;xP(8S)@!i_{8L1>sLMF5zi-e3xSKPbzAwWDEe3Yvu0OJU>OOtg7~fnnsI~ zvs!)ml@EGYW;!9z!}U;UQ~wqRcjBCP2-%Vplq5m@#5<18o@t5BEEEeCROmoBMo>_XMibg-$muYoK^px7GHPxH8z41TG-GRvE z?zkH)MKY-h?M-k}h^Vk|99t8$Sm`5@98=er=ZbQ{9+vO1{e^hGFx8LDnnGv^op`1t z9ldnO`9k{>s9&(G=?cn=1+SlS1FL4V$jGP^_QurGo~>0N3ah-`R>-W}UZ4x-D|;I7 zL!;!?@jj#x(18TVih}SS*A)VIM_a)N!O=em9IjoVKLPRdFmFlpm+cr3`kgnsR&|Zz z=l2jXzKpAl4VHnRWN2nk5N#2cAdWDN=^|oEr0i7Q*FF*?B-XUbk+1FoS|T=dL&+$1xs@a_B6k< zcjnjs0#G&WbU09oJCx8EN{eIzBqEzkPg0ibLsDI?Lrl|6PvOJDNOX7Hq@RLbf=?l& zMSsP!d`#PCBxsR-Eg#?6+G?t3Z%mSnx=HruFU4%&bwZJZHRjm8(#eCypRYlJJKTjo z5j;EH=h&SvWy zd$>aHpW<(@ZIeM_{uS_WYkLOAIXhqwtl4*8T$~?y0@LGapUJFUG;NPM5vVi|gI=c9 zc3w1?AzCNoenMe7oLlLj;FU{du`kK`Ir>$!LD!XoQ|VvGq7gW=K_56q13Kv~n9fs3 z=o;mj_KIu_W`1^~^(DGSWG>vgwnT6Hs+IDjotlls0AmgQIIs$jM&)8ez-ahiaYs;N zp%`N0QK~&FflGdUg+k3?byDHeSnTza$xRC@uk0UKD;FA8D!>-@go_HyvmE|4Z?P#_ z%M}3l=-vu6+`@PdO4!7QGd9rLU%b$YA9-O}068cTfuYiSu`utTe3enIm36<|+8(FB z%VRO$83p@S4HMjhW2F41RhP;GaekvN>Ef~P7-VK^Pj4^A6>rwv@4QM10JWf^ID07! zpWeG@RsDI@tIX z-R(`uslJkvRaxMVm9jgw>}DN=Df3@nY12t~W#D7ML5S`OMdT);?A@{a_kulV+F>%> zb5*OuOkDhGK!AmnOsFBhgLC6)JW?P79Y=Cuk^dUKzS^_TMU|LUL2E7J96C)jLbKQd ztpKOclK4l*31r)|@2nP=;+j3XWOYM&>F6)W_-}JxI-LEo)tst|S3Fdd)m%|fha1IU z_?nGK6%p1h;NUrF?GOLZQ}SE0BIE|ZVikWfo($Or+v#FAqlF4D6hhAuAr-iwY$*rK zTk|tHFKgzgtbD$zy2y~S02L~L6#DcI&PRbysiG-lMEdEQrqd`vk@P0*6%|$Mj><0K z(+hF;Bj$1|&mYX3zZI9_+QnZmV$NSb=_y#lpHXdl_ij12YN_`5qHgvVw00V4a#}%i&XnBOMVJLyl=btsA6mjnC=l;zA|e++8R0)@ z_FoJZ_Dau*5eOp;dXdHAsIM5G(B`C2e}xJZ|P2lylq4i|2{}c)WZYX`M^jYgSXFkfdCNX{VcG5OAAyc4XwHKsvVE}5e7@Nz) z;8LC=ii}$CG@@pLOgeB7AER3L;NT+W%*@7V7Q8i4FBmZ)JLJ30iaamE2kY3Sg>8Ze zZM-4gf`+`IX4@Z}Aocs6ZCNa#`yWX}LO@9LJzkiX$>p_FTn7gF6==(rooCn|NkDyk z?{GTMLm0Q}2b~rc`jr3Zb^+p8bB^qPvfPqA02>vEzC2rniSHe@(p|%5!^s9CbvGJK zi5@wwK#MNBNw4`+mr185DD5@*Xv$t-b|4rYr&W&!hOvS>7dd@5rp2IQdQnBZ9xr%3 zz?Y$T_Jj51LLbvFLwe!xjrN>^)D`7&pV1oJOD-hZ9!)gTtU@mo%cHV$d09K(m*6M@ zF49%DionDJkjE{W;jW}UC!ZJ-B4~hFOg;J3>t`ozGcG>x+njfBkXGZ}3MjL~tY&)z zTcW2TdL~936x^^!fv#e%TV?;^a7MT$056h0dfb+YtSc(Duir?Lz!wp<&CY?08s?hr z3k<4xTLp&;dBfj)oU>84cb34E^ShoND=R5~Ma^zQoz6u%J+@K<841jf@G$7?0%Kng zwFHP zxY@)6kGY?q$%AAG)}-Q|)9qch5<~Y$zc;W2;`M=HXYgd2OZt-QAX{ z#qVO;J3v}7CBYyxvwClMp$3Ev%=7Tc?%kUdONaV^-}m*Q8lSc(axojn-I_cjs;3#f0+$p!nWZig(bMMSQ7*1-?}5qgZ?-Y330jNwUf>((*Cq67 z1x;6!236>t9A=&)nsKT~PreAS{0FSaBmhzNYN9(bc9Zh6z8}(H`Aq#4)i>s2dxU6_ zvtvD6idg1yuQ8=zVJgp#pa4?a*G3!?jTn4VLTZX0WVAhmUmy-m4OfaQJrW#n@iFT+ z_OyK0c?s!Pc@YNZO27%$0Sq>U@Mw83&J_*b4 z;=rm%hZHB*ipMgYs)zs6{0tERD9ELEch$Fx{XMtd`X7*_d|jw2268~Q0};)o>?8{l zk0?O6@n;0C2Lm)LsfoFvsw*MfzpOZ z)7~@;PY_WmM#f$R)59j>W?9ig@!UwyT+6T)>7oeq>T2%C*B*$|NnN(F49F(Hyzoy} zsi^6|Hhtc^$tUZ7cbjvTEF1Gjruz;nZTb0|dd0CipD*7{}M;h*#AYpcT#)|*HfM-@U(ht%0r zt^-}gO2J$}&lZhGNyjIz@LPBvTB4W{n@z6r@H(vpqnMSi8sOH*k6@y#+oDz~Z&0K{Lv zxmYx4mTj&$FBRb&;Y@YNoL{<(R-yCWQ{FDPxAgjNZe$Pxa3giX5IOv<#FqCHUOA#w zuXPaSw*&ngv$H7^BhfAxW7BArO6i*xy~Ln zV4>!CaYhD*q?$LRY3#ydcSlR^4V~yjZ zg#qs7cs(uL_fw1YTeNHEeBqoc+WlVvzyb&WQ_C0pw*zUo2GkdV4@^% zI$N*FofoUT{5Ivn?$w@tCzq; z8qgcqb820Slv{f3bJ2YhI&&qK`IdYAq*Cb4B1wYYA$2A>)dNj@44y*8ie%=hPkTC9 zG*D^cE_1q#xo+SSQ(d$<^O3|@QrsA9FF$R-t6{PcTjJtLFf#iTuk*g9-0G+#!~ogg zf(2Wtq32U^N$mg+H8W0%rCFi}Kh`zlu>SrwJ*Gd$i?(X}e{-=Z1=UyhuefCmxJY<% zQ$c;}g}68BJB|XwK&QEV`Mh*b?s11(4PG=QiJ($&Hl*bYQp0gtuDX8_)P538dL)cb z$Csof*iK1K_$K+nnGuSY2RYgI9sDSCN|)H;IYVv%O`+A++n4SoCL}n~XpJPgXHjSF zsGMs1`qbA*c~qAN#L-U;bZkihZoa6W>6B3F^IJ|m=K|<8Z?t7>)9nV?{{n`EvtCew z$t2eMR*Mz+#eL`m+Y=G*B1+D~OT<+@Mv4JS?+K@@_*nOio%1;g+Wd(#(=OP666myB z`tp*ybODIDBBp$K@GMsczbR69q$F;X+)B>1pyrDqPZNXA<9bb?>fEz5KeYi;rg(PY zV1ad)ai&%VYG?bwD64Dle~=r*K2D)q48yOHS~?SA#Wp#dy-l~4=!gU;-Vh6^1u{`XNaak zLPBC}O3p_88$HduzI0YGF)^rvLesZ{U)1_aiBi=Yv2!*)7UoitSvaSR!2hsDI-yMx zj(KWPC~ky^^i7b&r2H!s>R@Jy61R$oPyEJ^O|!#((U(mO@xHjYlnLptao1|i*`JZI z$rQiN18Lfe8$}HY;%8a&PcP$yCgcFVa`hoM|VMPHq9ll#l7fBQ3KAfB|oT(L356t(7pAE`MrR~DBt zuGTi-2_o31!LX~|47;^CDb^8r6Ns>lbtE$e$&hB1d0o9cX}jhQaTKs0rcPG|kcJ(= z;eyM7Y5q?Mcmr_myOj#NdC94z`AJOBH;A`3vwW4Dms&RVwruuo=*#vT-V2F3DeNbd zZ19GfLn|AL<%O$9NHI~QuBm=1&(ED^=id)2ua25|w;33!&#^g$+*UOe%C(C?gMyc{ zH|N}#BavODQZ7yriRRxw*+DieO-|u!EE;||^~3iKVVf%(Ea=xs9Z_#vfy zZ?-~1_<@Y8pGgaEdKxDK-58gOj(!Uk+R*E&FZYLoHz!mm?S-S!Y1nC863{PdU+bGo zX2mL6aF-zUtGvdSeS%_mL}%}E=#GvydZDjl6uLdc8=SZ+usyZ%7R39_VsJfV-l9vI zSye>Vw{Q0>in-LA(M|JhD{XVpRl{%rGu5DA_KNIC-g$%vizaD+IMB#6Ec3~W%4Q;Z z?WOel~GwW5=U5G>;{JUtw{UBAMy<=5msYE)DN8sYOg*z7gHZFvykF1E~wek%0zebsA&1luO zgC{`>6B!Z(Lcx;N!^o>^%-a2mnAUn))p{3uFqRJjaUND%Ueyh4@9E>8_e>LSwC8or z`qt#2QZfEWnA_o9JU3JaTTO3S|8B+N zui)0j_YPm8#9?_PTgf5`iKC=&J{g%T?@M)BK^qws;YU0(r!%tW zof6BrE=(|9&Vk*)a@y>e_T>Pf#S3G2!9{QJiX@Bt4x8@N&RoifII14#DT}g{V?D6LTCB)PPR0Iu`o%?_jc`dtMF6TmYn?9*pClRy-E)q} zuBo86cG{N}(ejw>OoL{Bk71^RDL$A=DH!v1Wj?m;byql!Na{(F0DOPLN?*TkS6)) zfk=8GiQq2W()l@P#hvPN$JJ?|6ousT^}w1SqT2+jyqh*$Jtv?B4$d*~7`hAzR{b`^ zX)zwSaIGpQLO#0g@)qfsvRPkc8m?$=Ws|`toJpo0FARn!CaxTIP*dV3Hm%t*BNr2) za;A2kcUHCnQ$Vbx9Sz6bJlJw!d8{qeX<#q22X>InuIY`62PD3h}L{0@iAxT>XH zRSn~pz!l0cR2b@x8*xN?O~h)6N1?yRR3xm7-8ulgKvcT%7FW@$$CGI2A2^jJ?xbo_ zRSs}?Bop}?nWqyQScib(-vdOjlC5~d{x>k?m>h1jn}xN8H$y6=bAI(|_>m3D$|Uq` zl}&kRX+I=VdYBQ39!4_=GlG}7lq;e9rBbEy#n#OxZ~6So3qEzOPt-YE5k{NG-c~RM zT?49C{6BCl@GwBsge2st9ZF>{eh@1Q7(ZX+5U>Kq;9DK)m$m?^2iLICc(2ZTTtxRR zOIK51rUz5sA!528FpH1DZIVf!lUk{k~z`c+ywZg54V@8nnOU5B{8Xv zk~)2Gy66e1Zs3?mCT~}b!)!oFT{p3xfE+rLa#vE z@NE!aR>Dx4JX@CgSoCGEfrCxBrCfY4*v5d|5-usvEPF#y8Gik0vF3tm{#J=xi?G7;~9A-7RE z6QX7IHyJNJkx9?-6BpyvHJ1PgTPrmYZo`&rRi8yvHzH3y|e@4=~%E3}EZOpPP zhWqRU*5A`TjfkGON^LYINDy3@*b(e>5U5I}PoIE?;l8XfYs#RE*|(&|nZs5~hJlqQ zMFq&$p?__A-)@ej*an{J`FGo28`ulnA-L!9aY^?Tcs(qeI=O;ZzgkfJ6JDBnHuZLX z1sGWZip(G~h@^!hOL&8R=S@br(EF+6cOa?(FG~es@HXEd%zAKHd(j9N65n`y2=!rn$%WoriFHpc`{AL~bVRcqWkimEt*zoMTrQBK zf?Ae#RZkci>GgCObJE;+{asG|T_Ie^Twq@%jf#w{=TT7%met3(Ll?Is5PgsjloJBZ z>MtH|$|oHUl*`xzDc;V)B}S3kx}Q(Do!m;c1h)`K_bskFv+jN3DyK@pj#sByK~vmI z_knlguDwm5n{*rEgTyftzE!J**q+|6;@sg11!@CLF42WC)u}m>HoAEhTa4q<3DN=4 zSxGkWvAAz0o-(7F&p}no{ubAR5y62Y_D7INP=uRLW~IHAi_smt*`Vxz7N?hu!6MZ} zyl+}~gb`Vhydc*LRAVhaCmr_y^Cz+|{LbraLcv2%t9@}F6288#(amuUa%O7LzotNo z3dU|SCe8lJ(yAi!i|#s~`1m^D@0^0^RrpKj?CH~RLFx?FfL1uuAJ$?#Bug?eCYTUh zoH+xWW#_W7&OqD9Cs1!jOk=Q&J;o>`?@d>y1m|I!<`{^A=bhTfz6q=e-&O@gz)56Y zOKf64TLoXFPP0b%6tH*ef9W3>V15uv5gbvDu)IHXH$nt4d12e}JptgRDt3QhOYPQs zLeRr;jX`v5a-&TJU5wPp;g-hY2OZyh<+$4JU`8HUa<<#qM+8(nKnXhkPJ40rR)WRY z3OwsxBBrZAbkbqSE?}3hDk!?TZS8KrS%P3y@w)kHV;_2%{$W5asKZN_a`(-O8NuGX zEG7>I0uAubtB@)d7M6yGn6Nj(B2omA37z1-3Jtb-{TA?Mcgru(eG6evv65<|mhp4o zoDW$KZKe4Hy9$#VzjU}Tcqq%3gsih;cES9W*T}+JHnu&58po|7?2at52GFqEc+rdM$-RB-M3wnHy82ai5c;-lNNj zt0_BpHCV8whF6W=*fsE;&BQ~?(vb#?Vx-N&f|Aw=@=Hr|)_Vk;JSnM&3zG)OS<1~MpM`E}&!gLWUKEG(E`)?1Jp z4Z;%xpI~Kat50%Qz-z*+9HI|*uyQ25+VL52A(UdbUtI#%6dc!*YgW@}YN3#9A4?6@InptocBcMSdrJYElKuTUrN1_R#lqICE&48E(vkh5kuszBy>4jj-P#zw z;f#zuNsP%^tzH)gi3c4APvz3oc!DgA9!E0nO=f~o4Cv(9n$+|ZVVCVq5q(g}%BIrX zg)-INT+P#G4|B65B_haW&RwY$A9uZL_lp%kQa*z=MWk6L?&pC*%D14iXf-lE@~q!_6Fq9&h#QTzNzwV3CzN# zulevc{`#4D!L1u!IJMvE+IRGI8?$)R3*SFfTHm~F`|f6EcBrtqnc2;jeNJ{Q9iKeU zZ7(u9gap}=acmHt8{4zHxi1^7s9Mevi45?|@m5j^argx2TG zcdVi{tz!~ZjS9achHXmRoM5WCC2{1#0=bT`nxij+A1n&Vv1Vr+8}ay5(6&>s(pW-8 zKB__Ez^1bIKMjAlTiL|f*~GX9y|FO8ihJvVxXP|pKgQv?0=Hg|y`cbAP41l$Fr8vd zjL^qBcdqKH{IZoKBw+b8_fg2!tu%SRe=YwT%%1t_icGSJ+rB{GY<_W*ZxolSAqczZ zT=ZFt?c8Fq7GCU=D!7GM@dUeEzl1=aS2-{42%z9%+GKoX zrIdJ~?bXo;m4u@Aq2QX<@2ZVqdw(js*#uB_k3Y?6OMVuvD|%uTUXOHm%PZDn2wx$q zrskPN=!Z{YUp}3<&<zGt*O6lM8A&RGq^4YdJhG+F+9snX z?Q5vj%{;Ou)*j`mo@Y=$`?Q7aywR3D#~%>wmc3xy5VGa=#|8hQ(2?QQdNxS9$k!j2PZf>j`5Q=KeeO5QIQA`_c&+ z$C0uYl!LE}#Mmp-$Fuj&KlAt1g&rBqILy0kmW5&(dA4-Qn-g2x>U))?W%yRE6_`Bt znmN+sc2z(iCc>^U?Y!a6`8U#<|FP!uV}q9vI)0yH=IvIIGl6f3Mjh#48hFS1s3uQq zV-tMs{Ch~psuZTpBvQXoJ!l~_@-(^d@Q&QjtwmQSaqpjsqbclovMI#3=!Xo-x!l0) ze!!BFYwS}Z+EmV@cL@>(7R~AUw-$nI+H_HN#wd%yP>_K+34~97kvR< z`^Oad<}L)ZrS3r%PzunJ$ov)9(vO5^32_F2Vm0J%lu&^!>EaAnLWO8bGlFt}?B`-x z&su@c+$Vfc5Z?GaQj}`6(@o0eX@R22o7Pu48E@D^5G8Q82j74CPxM0+a`@BtAM;-d zm`{AF9PFfUx7xzp`7M$Ndy>{C`z$Th z06piVqrx@=hP_R7pne@!jcKe&$Bs|;6!=Ywi_fjP?L_R~GQ<#3 zw)ay;Qdwt4_y)ggMmQ7rq)O|@EKg=%;K!LAID>f+JrgzlcMJsm?M-Vwwh9_`rjR^4 zW2R(+*tz}tmuwucM;DL2VBh>Yc!j-u+`&i>FYs4~uMe{{AvlH#WH$@xWGr8F_Ppf2 z^Gf<}lEf}=x-Jl6M>+lvdv6^TW!Loq3xbq{0%A~#5X68EQ?6dc8|8|_i-VcK8>bGoO zrxh+Dc?c!(c}9Q6!cehhhaN+(UHe3<)02bJ6)(?GXDuhoo$|p&9`eHk1J{nUuqiq; z@L_?(^5}Zr8K|Ghpqb)%CM9gBQjRI&lNxZeFyGjcK)OU%J5Nh`8Q+fbCqQ zV6W3Z92sXCOmsG7D{P_RPaCN-Z5&lfI>DG!w_?9QOK?W5@QsTX=9D14SEdtT#XTlj zw34}_;5PxLE|jq^JLmEpRDSJT02)OU=DE*ML{5BiUFq>Z76%F%@PVvUVi=8fWyV~* zt5)w)YmA7U$-w>3()jXQhs~?KP)eBA0z9s0mz^nNh2l3T1=jk4_r2$r1f9#uzY`n)0T+8pMVb94u>16yo5FqNaa}YIyS%wHlLgvl2VZ1md5|6^c)? z@^-%Rp~bnB@fMUJgYFLL#t8ci3tr0r!j>|-Q=z=%Lr>2sl{zBjt&VqXR!}E%Iu6H% zde(EhwPl=yAE_178fB;#dw@tGQPoq*o9j1mp|y0mfz-tIJCc{B7d=1rV3Iy?xiH&W zJizSu3>4zd1-pW6Z*eKIb8f{D!ZgCZFvw0H>ZGUW6XJN!3FY^`m6>|GqGV!OU8~fE zs3ggYUhPI$a%$ALI#(t;^%o<9h;#A2W>ip0HP<+MlV%+G2=52E%3BQ4|wd8+YE{$z8QhE5C zLGC5Y=IiTXlAt9-o)t~j1c%;OJowUUU@rEWc@DB49GA)}5o9Y73x{&KvUx^c6W1yE z(Tet$%Dl!V`~Ey#%UOx}t{Q~}crddiTH3NC;c9Sn95Qi@!XY%vE$%>!kVCRRe=K`V z)3Qg6zS*nD-6Q}<;7WPOg=>}5)mMGMGjv1Ex~kj^sUkJcwKbGx#2G7*m)tt@p^?l> z9<*rcGD3bSN|M4Bd>V^jE>X4^89ylra;)ar85m*EWjM8&xpmGkB8btT{;-?!0We<@ zEuRItJQ3V9$4`@eKQsrRN8P#K3yKXJIUU@lCY|UV_-?_i5Vc+vTP@Vgenv*Sz~wOR zD>P~QV#fj>kJ!#lE0-xRX{CQxrP@LklI0`{7tI^}~(A zz12$V6R$m#KXb{b`G}|o>dx}j;=N}Q0f~vO{gcr1+41rF>mPd*n zPqdmCUA9Ruq&?FS@qoKSSLq{NxQwU@C?oFxv{IBB>0EJZXL@+YUUdv* zKqn*eq6VL?#_w!9YxA)@eBSI*Y+0$SJpd~(!8N`&;cM^t;u9M>20Y!WmGBl)5i>GO z?lsUi65_A)=8GFPunCZRk_wtnp$&*%PEl%Q&`V2V84s26-tVp~eOHk}%EX*r;~&4V zzt-Is)|)@4bW5wpH79`owt(p!<~!jRn-|P3mN&iUd`?Oy$*_;^)d2>FZxg!eU9sN` zR-8-*vt(R-HgZ%yW##!@nE!cA&t%Fyk?&)9opNJdFHcKWi*FC^&P|t3cvHh(@Rfzn zME>GylhT;3*!=G`V_rJAj*aV8@HkK`9xDhxO%|ja9&55Gs{PSbm$O5*0L@xETp}J^ zDG5#I0GD-)D@T5nv}8-V^u}}%l=rFMHEs>@q&0K@GmMjer$oI(9Q(t#u4Yizu?dr4 z(`8-)w|J=jyjHjkGPuCj+jW=xH*i|J&zKOMqMC&3U<3n2q5Ni9_jFnC4XGmX?+a!$ z%x`;(#opx7^!3rBSPO7ldqhlC9M3mc@Y z9XErwp%AC9WD4evTPFBPGuxgT^RMGJ^1ohsE)_KHi6Ik1QL{IET#0&;ku=>qSlAq=b^_W*Cr#?{CN=&sK#iglUbqyj!rImdeV#9j_2^@EBO{Q&Qe zJ7cAM-U-5;i(9nK$4%9%yCW#x_>+++kO5*H*w)%f3c{nyTu90^{>wtV=N9En@~t}0 zLbUQEP6xc-sWDu68Xn$s1hwrK=^I~hTKSTV!=o^noZCT2{6u=lG zmM>@S&ZL{A=+>z{W*bl`5i3ox*uD$IEC4Txi=(Zvn#3T={Zy5_LYN!w>FqtbRK91I zP;iaP^EqlIIG;`Cx1(JoxbNsH0+W;UWT;eY{w(!y)`^<5d6iLUN+UV0v$tp8e~nlr z$pT(xOGPYYM#FU3=xe+vbHbEQuo{xcyYp1!YVflVez&YHC!sBukCYi{{u+shBCk|i z7ZmmK_lGpF_BQ5eBk>Oqq5k?pksl=-P0h=7F)`!JAKbe4%5=sqi+*|htWNKjvaJKn zF@n0?A12O1XFev3Q-!G3;m??Nypzb?EK{)Ey^qTws%^Az;+&Hyc5?A&a!c5z^R7ZY zFB3ix$-+MFZKTx{dt&p6On9Zs1SbX!TpC~(FteUrq2!P}C-h>Im|lw2Sq@QwZFV`V zETf%`uwm5F+kz*v$B_qiM{+*?6@novai~u>6!)lOP19EfG>n4}Eblwo^v64{)}b!P z($KH`oD*(e(B4>DBG*z{?5`l*}Tt7f22z?G1yySmms<@BySvrM~P&FXP!^^@*HdnkPX zueGteW^Ma3=#2p0OFN%cWnsZ)D8a$OrM9@AyX0XP(S{ug(`)I+t&F$~%^v?fKDOcY zEKEI6@5*k{)xOKImXC_uZv>bI$rTU((=Hdga|2t=k?1R>TrAai`*A2xI^fhJ2Q#8;|R zv<6;ghHNM_>$>}mjDff5{W*jVVdFY`=)7J#^MP_Iv>EoQFR(f74@kmQNt>d4DVafA zc}ZHQM|*qT1&)c9QtUfn*D2%Rl_Q*T)tItaJN&#sk?zCIJ&TD3H@@IuangCNV_ih2=7#%f}_e=^~$R-_2i$sEpsHL(Tuno1+AG~KfqI-`7 zmZ?Niua-7T-m;z*jE&86L+A@#+g$pH@6t6jL$K!Lg8)xMQB)jd_3lohXM(3%3Y`Ze z1xL_%M0OcS1a3zm_1GpUPn_P8ZcV*Ahf`eHS}wtYat^BOrlH2AEKY(SdHaj?`j1pT z>L+&Dco&`>(1ov5ziw7ib=mUtl6zW>=X&r5j3^P${Aw5do8YyZ)BUO0NQfXMqD@oalTt}hO`1?YJ<%FV)7x_! z-bcrZr~LX@fZ2&zVH2sfFtvP=LTbU)tQD*Ht1FyUh4}}N3i;RLDw#S2$>=Xj$r|^w-&Gl;sbB}|&9do8q z;9i;3RD`UV>wT&(yXK%_Wu}cbYqnZaDG6RVxx4N<5E<>j0{Xa}!^H3N1aB=g?}tjm zr##Cn9_+|3qBMD^U&`HVqkMSg3w-hb_<1cX8i)>aag}QQC{LQDl_0RjPl|cTsl>+2 zv*HyQ#7FQ(d&l@wqS1a#I+%LI!p|c_*jMuGBk$CT_1!qZrt;z$^fc19cISMu2;HKH_yH+#mX;q<_vU?a* zQIbnPQxW@SN4WiIU5JL3#(ojz?pZ>Smc$!_on3{ERXs&P1=;l(liELBMk!sAXM;9? zEsBQm%eJ@I0rRLbkEF{&3fyDA(aQF4NwRP7Ww>L6uEomx8}r(VA+V3UH1i05Rj#UT zue6ou@~&&ha|;rioJDgCn+Dsn!OZrP25ZGJ0*l%(pstzNunbKl`QSV46j`5Jy|2w& zS~mB|XXDuOZS}#teHj)q{PPj4Rk5@i#cMru=Og@4oeDGI#dB%tF$H+rOcC&>kAt(^ zocErDeB>o|F1q5mgS|W=;UJ#^*9@4a<_hcG$l_mbykEU0KCefpH4sBZ<($snB9bFj ztuIXnk;XY0cMId+J(shcE;^Mh9XBKnkY2<4AEi=Rb0wz5Q~FQWH%Chn0)tD*X4c=t zFoQH^cQUIM=>QecWt;K=lkuoZP0`SNFDZ#Kwn6M>C9hREHar9FB5FhFUnuw&gO|2{`|T7GQ+6+62&$U`GLKer!QDHWn?}+1oknS?Xbz znx!WwNOh#v$0@5nZ_0U&MuEh8z&y>*HGWqsNaQhU#DL{4BuTj*_%HU9=5tE(0=bLn zdD`hIZMLF?ZFkTZcTbr*Z(9+eYnS5^6}G^Y>B7*T_k7{ys6xKy%3O z!V{YF#q}d|np(W2rD%Va3`u z7qrJl=P;qwV&6<5-85XHkI`@WAQ-2Y;+4dbwbO?MDRM{MTtYn-Zun3kB@;=t-7l>hs$A9Q;-t;NR7Ans@KaRA`wxW&iVh7w zlJJU(nlpXdSVYlTE;)bAQsya)yYZFs5wR6JEWbIsT(!^wE9ZLl-MHC%@rAn9i)++K@GzHpB6iN7i3iLd0QKgfA84$yON2tY50D=2_^o2K~-V z#Gx}2`#eWjfMg?FeZJlfWGK=D_ir$d3}v>L=w@q(2eo{_xBp6Nkh&AV@et?aZdtC$ zbosHB{Ony7g2S$p>4n1$Gpqi0_9EA}G)M(s?;Bh_=bSNb#FR9s9_V14ORq&{UE%JfVT3uVi&4rnF>9Bp-)WTNEyS%?(#O=D}B}Xxj*uONh z#-!4V6RAEgUrt5>5ER$?Pb}K7J#g=2!(_ko{>$G2s{kYkhTSn*4>2|P*3uvL)e=FxlsYrsqGo$ zmlCi1|_05Wj`*@`>f+z_@bxii=!()-|btGYLQ zyztcE0;ZAV>nD_|JA81nVo!ld@q6RRKpPTqR-*VVFU9y(bDu;h_f>?8)e9dic1Z!n z5;;}(=P9ioFZ+_3LN_JpwsH5InWN^Q>ZB0~qDG`!adYH+J8A90FugYT z!#mVbmGIHfHqo2>0|wmt!NCGYKw z@tvL%)Rgt86|c2}Z9nyzt$0dS6Udz0Q;Z0b7kFMu0&3^aYAJ#SLJ|22jEg@Upmjv- z%|)ZuNZry~kVI!#q=j*u$ZpjXftDtad7kQeqr<|U>8-<^jxDB$M0(VqVY}gBNrBX&sj#aiL*7fpcJJ#ol4l)GY8?9sct8B3J>MW4TV#l?XYWYabRN?Tvw_`cA!kULHNFyvfA^qqm}b^QEp zZ;Y0l8dl}NKbqD$gspIAhecnV>;Ye?{?j)7?y2Z^B9Gj-x`|zQS6-&s)O_5*94CDDjl^WaQD@S@;ggVOS<7 zuFjPi$Kl+Pi6!B3k=D$b(bD}8`Z>dKzz>qi^NDQd;x2%z)W6-ECO{AR>A#~RijJ?Z za-VH)?@F=d5<4s=C~bF>dL$q`s9P4&JN$O%hId(l<(dWB*3F3B1K{_}S6t(i8!XRw zCM`)|?UdAEC>WV0v%KneYp7T3C?+*mDaK2#{$3CrjABAAl#nZQ=sCt~5WS{zUhxHU<#4qAn6^AUog>mwp z%>7(j5%UQG_=a_Y>PISo)XNav7U}BJil;jxE@G2kv5$Nm9>VgI4j5iTMP@romIZ3e z7d;pnKFcr&(=cp!D94@#IZ^ZVjH_t$zq5D-D=3@v)zB4Sjc*-!KA-;^5Uc<|=ysmc zMT3ZQv#8pzSMVZs3+eZ$TKd~q26Ogsddk-i?bW;m~U-KxNO&gpN|@t z1az&`-v&Q_c3y?Pgh4l42i&<2?9WS#Q~e^9OwH^{eGSAxY+9%2HzJkK`jU@>;;p|I zkAcTTM!u2iV3`q@2hf<6-QF(|8NM-`Ht^%CH8+dB#zW{0H~^JGQ}H zci}sXPwTq+wG_NW`*VMZH!6Vva6|Hu89qPbKF!R{~ci=@Iv zz}Qj{5gu}GPFBUyCA~SLK2qT0rQWf0n;p4jF%9accie$|0PaWq;^~gcR(=w+2!M_t z4O$D0RnNrY!=XBx-bT0BH5sSs`iw41*)Qy&8{(Yf9wH4{H;Emgc-+Kpzq8Z-TK2W| z?x#$iX3}X&y;2R4{CVtDZ*v>Ay0_w5r;5XtkAtl<7@A8ksY_j`we*501L||Rmu4_) z!?E@+x{`G7+d(~%r$p{LRq^9{uAffadZ*S4=YdLe{jLJ9Uc=1qh8`g;0ImvkzEPle zp2_Dc*yq8QYRZ>9qLkd^9cGz4JF8=ahw6vbzLf4u-BW}Dg2yGU#};7Tt^utmzh8E+ zf{lAGriwpz&;M0`ex-`#TMY)itDo7=xeK2upxyIA7?nbR(b?U=tiIBF$D&I!-6(x- z`ot$UTR&iT8anX3R362foy`f(mfK=6b0s_%B}v3(4ABWbfiL(chFb=hZIm5tF;q#u z*2?USw{3p3#>nCZd_ZSLNRj}%CNcGYc&0>^3JHR@HT5*^)IdGDoylj0GuK82>w>wj z=s=*`R$1}qu@joYFo4$Y7uHLg9K^jZFp=LTAKz=OY)}M!Qe5o&6h!)zQ%mpZfV<%g zVxBF$%j4RxTTnp!L9Ux?_Qo>;&jk_hrNPRxyo{I!ptB7wKX0PTvmB+ECkD1(iQTov zQiZ{`!Oc4W<&%&s>Is^)aX74=tUYI7eB2nZ#!!iX(~VRA%HE-=y~1e>;)$oSFH#K}?uY2vd%X#3nN0vQU z*;b?rU7|l@gfTv~z5J~T^AAo81e~tq?hCL_Q7m#rWn!vFnm7Ru-*4rR1w2nI5Uc4xtQ5|vIvzib`e`(VTJ8E7 z#_dLBnJF&`WVveU|1&P^m7X27Sz@EUP>F}s{9PZI(E9>XPV75Gl7qA(8&b9((pSO@ z1o|?H<{lX#RX{y)RHSznJ$V(s*#9@z@T!)dAh6ib9^PM2WixsbUqJ(^?aePaa0nzj zx(K}h;`Owaor}1M4)*T0xu++TB38LIl-$8}negzjZk}iezdO>>EsrjxPn8K9>wmRM$klT33w@|(r}e1Z9oF>A85`}jt^x9c zSxd{Nq=;sqll2T{fw855#O?_G{h2o!S{>vaWZJmq?Pfy79<37fk3V`cJSh;k{Q>8# z6eqKp$kP?X%SUqE7SvY{+HB@L_F7(}`8x2RYBqqqFW>Vx>shm7!vw&Eg1B;OPgLlj z10^VX=&_tg*gC@(zW=Ku1Bi^xF#v+R?CSuIyyW%s`bZc`UozjylT%RF)j|oRrOFfv zyOunu`l?DGn8;X2G0$kY|401#xQnFG3_3vp0Qwe?y?;5*RlAJ#Sq^NI6!@B&o(mL2 z)YL1C$;qayu$-kG_c=Jje_Xx-7LmJiplXv;6)C2W{0sQ*GyX3jQC2vEa`!`-JS-?P zp7p&)#>^sOgs$lq)px^=1BVL}h`WRMMZGMpm;LIuVgT3&l=)mm;bK}o!6DAPtBRh@ zClm5siT0`iOb1>Vb*C3tda9(1q1=6@%kP|uSIc=6ssfI z_5x#tQ3A2!oI&sAdDK_bW<#D&Emcqm={)&1NDCLsm!!IRIc#LjAK!F1T zBksa^uV!wog)=;W6CK8oWk+@5gd?YXf_yM3pYCLDP2S$x3v``f`?ay?uuS^qdF>o1 zmz`kPR1B3Idr86(Dt-}x!G-Zyy4QXwlI{yYk(NNEiyt_TFdfaJ^}gwWPv0FIHiA+z zZ>PItA#9$_t0e|;zutf%GYfnvCp2|S{k40QK$mX-F=1gj=erR)mcTl zKC`(}Tw^c9vQ|hYxbSs2Ux+>@#HSnwR?rshE2bSr!s+;9h`u?VmBrISQQz zrzCjL!i8A5em4*ycSNACDicU+s;Zb7^{v`c5JxN3d8saVr}9deVs3Dho6`V@8~e@s zxnsFFl@fv6yTUu=x<8%L`U_%h+Hd>)R2H4TC}{3wimCHQ=~3O8_{CQ)pbsGIudWMP zJ)*XNRYi#(^7`1ALs&y$`3fy22uHz`4|@j-G$XJhhZ1R~!$xU!a@g2Z=l7?xw;1yV zl^r%e0TrtY0`vj9Wd)i~EQ(Hq{?d+fNWLDCE-#sk$m z{z@OfOb;|p=%TM)=TTz1-{yZ}ka`2wxsf(BUaY#q`vPYWK5Z7NJLe<`X%VsgsiAzl zI((sejhM#2qrN{Sxb9#3oOzXa_hr$DNW)QZC?*Fs3<6@bOblgZtf#ZRdj(Q0CS1?{ z8QW=b^$Zl~yO|A(*qG@SLYtEF9{2xv}8xWPZWZ~LXL*QR}bIIotwwtE) zgd#h(?LDfcux*(u*oIfyOc;$F!f58Qk_k@QWZ!YBJfYe(4`fcMmC%f&6yI4$&kT71 zo(LFeZkSMdy0fq#FTpd&szH^u5j@F%#$Zeo0f=Mi0yV$>Z31VawX?t+mo`W)Gu7Go zxu8>?IFu1HVp+9FdWe0Tgh_S!Gh4J|Os@Z=J=Ys(<+_@_-=Ym6(9X_Ab|TtQKmFk) zN{o`Mt;v00TYL}Tw*E^aV+X-*|4=?l>64p!0F^plnTZlGwu~Gzj{jd{YhqS!R&y~dh06p&sT*?pN_BKQIZeIX4EdcJ=d`Gx{ z+7Ph&Kx_=Xyr>vT^{Hsw&dd{A_~@P+XrTPLz$?b+$FgNZP8*`hB6It^T ztKdsfq{EO8SwoFpDvsR=)5~Scpm~N|3ZE#*w&)ZYMdq2*81d{h&FWDaW>v3C%(M&S z*jMXM)Hv2#G``nSt|21!YB1Q^?Vk_wbpM{C6`)J?GJ^BG#7hzZpkyum>#4s0tKn(w zofo#;yNHDfnw{0R&FP(e`1smtuZ*%bh(pEms(K1m<@z8!hsc`6_X?<17|lh%`kB)2p&kYY&iH)F?d`QeY3nb(U(1@f{Fg4@&;{{hYHQlFQi zag|@`OGa${lm3U$(VPL3Vm*$EdgbPVb0H#6MF4({!H{%iotoMbwDX8nE<2)AvudKy zbBA_unbB?O=?{#Mo>F6wb)-yFb-pu#4Q`BZVb{)Yy!nXkv;aQe3oSfMY)f3tU>N~> z4$(iNKjplpcW%q;z!644-FNTw&N_U5u@$uPPa-jmb~L|v=UEfNd_autlbLOdjh52t z8a`2hoWfp?vtd=G_N_cBa*Ll1Bv(rB^m+}q(*Y&8haS7j%rSYd(rKgLAk6*#;iah(odjaPollf1_&Mx&op&D?TQ6nk~Z4sg$ z!=GEfB0$CI82ftP51d0^D0kjq$v&L^<|@&=>c=Xmz!QWcln^7&J6Cs{l)E7<4 z7n7)^z>Hh7Q`yYmTG3Lb$Z`PXh9an&S@FG$?VTS#$dMr6mIiGlWC!|%wQ0MSrJ(ZQ z1e*dDt0(gVJcJF>)+uBSEhYu!vdGT`y!LHR%912z2u_LvkLRVN-p%qTUTJ<_O^6+u zq{|eV`+io4)9g$X99TbG=g8ksCI7Jb3@oR1gPd{C4pMCGFLD#*#*~+O#4zm5_|(cgw^y9?*ypcPHd; zO`*NcS2-@K7|8$>RJCo3h*UJ0(&AYgz>Kixno`$ZXXM<5UNRQfpti;q7TEVqQE|>l z%Z;uI3rfnBl-i6?w-pNY;kBj_FQvxYSc@+eN8UVOj(i>&o+`x*oywifjaBR4km0m` zzfP&3i^0mPLobnMf6OMzz6kNF+Wu-^PpbB%64Nqi55f0kU=Zu~qu z5%*U=z#rxgX#RM*hx{$#edB+|b<0VF>90+H*woyqu9%T}Yz!z;3dPKe*J~2>bDy%iT&)#k{ApY1`k+tVBlO`=3>l^(DG;GGq{+ zIR(abEA^2@UXpc$-Qz6_yPo}Y$)kLy2sMQT(`XlKN!iH?&^4g#p_icYheuW*)w$0p zBmu(38*P!PE*E}6pflQ=d7M;0B1C%kXSAu}*HE9SQI%Ua$7Pej z3esDSO;-R5P|1Dwgp+j4*&UW%+H5xv;!77d_HvKG?HeaQk^4(Z6UVPCbTn(NQyP?t z^Z{`W*($~FeQ6)Sa-^91>o2nv7VXR;6rDTbFsZ6TjGreMg_%U0XGQqXUGDRVU6$yK z;f0X^uR6ZgiXpF#cmL=be?zXa*;*(#uAv#7VYDMdPW(e#4dp*?U0G4-6%mp!H+yny zu$Y-a82^ZaCcP3CZWRUPdE<3ASA?=PT?Q?w$?z@H#0d#UvF0Nm zTlj?5p@U(@;Foww0K1zE`nR}&72^>=*NE8F22|F9FLQ5nrSyT@OjHt>G7*md-mEumK90cQKJq5Lp;u=cUPX^=zhI6)p(F zH2A`hQf;zk#((dexOC|PtQ8r(_5KfCL2EP@8wu;ckkmNxz**|=QCJJM=b?a=O39Ii$#l3=e7F^} zZc5qrOoNIF;#7ol*^*FTu;0Bdbmw=zj)55&IX-X|_vvzutmo-+co z@60T#)M13p7Z~g-^KrQa@KVz-!lxCtYEN=UYx1h(>fa=PLOv6H0i0XxHJ{Hr)AKHL zSi=8ftM9J~Z{6jf3>m0Mwn>rcLXqvQe+`BiY9OgnU*U|h;0MC0dR5CZC2EX_mjo@I zkLTy#{_IZDaC&z52)#xB`;6}MG9?l8+dd~9BX&rZ;L#)AE1JoY5^7=5MK}SqdH`-IgMl`lS0)n*8{x~>O8S` zYo?hsH;(3jZkl5om#2{S+bFZOPe6&xI#Yz|qI6|f4p1h!p>Wf42Xn5EXx*D6z+ktX zNeL{kuLd8lsmqp*H@H12P}NMh-NY7HT0+!a1UaVYMwyjPSMh&83R@wHVP*ZtCrFnp}T11oj~ebv4Ae?>gQ;REagJLOmM}5J#b5S7YMP|2%J=4tSo& zcURSm=S|9BD`gZtD4yrkXiw!bJs4qk&(Dyx=DVGdv)^!#sV53f)=d34DO(hP4&8E<$N^>^&&C)ZVMWwK*#9OSvP*74eYs zR|RYiZG%Ex0?ijH=fHsO=ljyCRi;oNl}|RX;r=nXe+_BISUl^7DdD`_JS(zOQ3R6uGfKyYqV#&65Td?V(1eA|Y2la&tCKy{b^2%(Z54B2C zes|_|sR85eA8ajMm``maF0^pegU6J)@qM;hc-IWK-i~+^XsY0g&4~V^B00Fh<>RyV zlQ}j0%-XeF+;u<*h z{>_*F?8-f}vDyrR&@FM`m_P}QjvMoc{4uuPG?0R!Ivib<98|!Am$R)DyT=YeAJw4K zq#a?eOe>rOS(hJ>qNYPmky1$CV+3!Ft=bLniUp+gy$|eO4C^rkjja6RbX2;n=*h~E z^Qrz&2~kOw=z=81M+(u;XAK)@HP=A9egcT@MRN!VP<^wti zq`=8S4KF%`fW~KMEH3h^=x(y#P6K%5BZ{tUo?azeU)Q}Rn0=gHqo`3OxY=3+q!!HN zLGJ7+v8%>dLeWMnYsWV2<;g%D*ILWHr|>ewVQLZTA~y;DWOQg3 zphv0@)(o{Or5OuqJN7K!Dm(U`6+f(Nj7DAFF$ATDNGhge56el^N-Mk4H&2KhH8=w` zl-1)9m9|XVr=32#DjS!`E^-X7fdl?@mHY?9Z zP@$faoW1%p)IH~OJRLiM41!6Iu#ebR8?WW+6gqO6Gp$ES)$xgUn*!Me1e4mw)9!`@{U|2eVnnw$AOKd^dtIn8JV&GP!m|^%Kz_8!N3}S zRx8aU+eB)~j7Uz-@{_7Cn0?Zk3aTlB%Mi0$s-U(+dvaAS*&XX_|z*%t^^pOoATlq^A*Bc0O>PS+QdEgeTtP#3zk znT4xw6iFHa>1Xs@rrpn1^XGY!3ynS#aU30qs!Eics!6LfYYoK6p*r6pKav7F3qE@&OvP(T`y{ZFCANlS{i7n!0v@@gdCUEV-b(81;8nO{9C=vX7 zkYBuj0#g~n#u!^K0je2LK|Rq7Dh_CyTL{^5?pfA+qUVBhIXX2htm|v&)F%ZlhZ^9I z)T3^~hu#FRC7sZ7Zj95jV)HE>2d^^S98eAk_8bH5a{o|Dtal!|k+=hc=EpxF`j;rt z7MzCNgbyu3I93d;4o`S*O1Cp6Pty>nwlhypk8HX*9@K) z7l%cV6D4yAwjt5(BfHtz*HyD^Mn~#_v&|ppHC+Aw-Sko)WrtPjS;QKk`vT{;a;-vadW}5!*J9#>CfaDr7Rx9}G-;l+Q zm;Si4fT{@}_xt!aAW-i6X*2%8mr@ntVN8ytp{HKpYr`H!VO3S65AoCi(?5S69iFz7 z!J?`c$xK6KCEnRY!|jrt+^2%mS&m74oNf%`rCZCm=4$&WFTeVtQ0#*QDgqBjx7vIE zh5-`1XTrjv#~jr1S&1Du6DZiqa*gW2@ADEuXsQ*UF?aJkVg4VShQ_^W z({X*5c9LKe&!`WK=f32WpQuYQQDuj)*#EIb?aHGr{4cM500>9dRoFcI_mckjZY{3!I{hE- z|A&UFqv_(#_Cx63FnNyHXFsJKu5-&{`}1!k3SpZ zZ84yd>hd&ax1Rj{;l%=csVZ^)x6gq2(+&c>-%!}|>fa&fp92E|p}u(De_6B7{|V&Z zQu#lD{HZ;F_Whqg{=X{_F@eEk)Ae5r%q|J@OKU@Ua&@tUhTwt|3+$??P6H67jYG|z zj>)BdAr-cX``ZBmR~w+gY<#PE;T+8P6n*@3M^8@1CBvj4D{Fz4%y@cZPnPIQ^Iu6@Ab1J{sDDI0-mKj@@Igs^@}-^?kKo|jFUX5&PuKpV${`FvJJ-nm+#;{18Qotq;=;FX8#@0CxKhs1%v0ClK=b1 zXa0zm1R!ocGFbKhV?FQVe@CeQ<$=LKcr*>Q5jww_ z=|2Pf`Re}!^nU{S-}V0gH0b|K>`%1#7qa~y2L1mRqkd|KpxATFlL^05wcm+(+=i!s3l*Asr}!shsAa;tfBl8@gFYTvSu4)im_GP=5zui@ z8!4s98OeFN_WR0G65w9qft-b!TenqfGjClmGgOixP#iMzfbikI>63tfQKM zuct0`-q-V^c(0tDYj{;F1O$x{^T_#sMV__ex*DZ5avt6EU-F$f1)0%V zbuw5merJvvfU9XGSyJCU{@29K@hS^FdOb{k_mTs)0hBBhJh-YInp^${wfc|m76VuU z^v-!6=MUoZpX?D(tOD{ZZU;BfCq4e_H-PT~^;_Zp?KAX`^3gqv%Zo-A)bhW5S1iwO z7d|KpDVe|>jmz8RJdrY1Q4vv#-T>3H^YCHp$j~>Xhm|PNn?@Q# zPl+f`7odMt>Mmghpyk7WUIQ0Hf^_r?o#77S)zuHY*`Jw*al*$7ij~AE1Pbe@thu90 zi3f9$CXm+h=|t{Box}AhhCv!sJeI&)XDnXxtBkssaflSpyLv^IAKH7ZD~Vq9{<(wb zm9N_abc~(%2QPZ_)N!Fq>Dpj8@VCM$z**{wk6&9NAY*e`Ny2G|U%Yy`@;aMmjJ6)? zf@_n%Fl)M$L1G_wqXcB{1WOLmY+DOFShzMk@G#~Tw#v6IrGqt4Vy&qva4GLeMY(o1 z(DPmSUor2eEAx})D5C8d5mx;#u4ydAR(o5i~4k@D|aQKzo1$04i6=voO4oYdiv<@FqOei@I@n(S;k)JVX?p_g&k?C)~qs^7+ zp9SM0H{&Ocfg_hjJBuf&#aAvL2H?;j!OOKHAY1Nx{YGr#^OQ5oxRhZzZu5PzS{vRJ zY~FOhrBm{zWD6|~n-hLR{BB<8qg#KlX#Hxy3_Es@9JYajQ4AQ^7)SC_kc@&z$09@b zjBonPpwl8vFmP!_*NrXzbYs;RqGb&u*+q4PqK4;{O&%5cX8Zh1s}L{8EF*~1eW!R{ z4w|cyJYXdAtZRBCzp!u|q}8*Kl>`FauOV#P1VQi>6^t}a>Gx^~xFL=#<{@6DCAawg z>OjW83e?B3#RR4aTpbeE`biFA^#Vybu|q!kU`+R0(uZgbW@Di*^(OavHg*`GofM4;wN#%xB|X}k;%<7OXHk3`6X~vEaV?( z@jlajigbz1Y{%*wKNMtO|H*HeTXij;3O6Abd)fYV^01-YivEBzMq_|Oz9NRbgiW`7 z@?_l&Oh1v=v6xu#TrGlu`lcgraH)T`wO?H=VmqPzsYr+h^)+e{9uI`4shz$4_W(r? zAuNnXA!|=5x%p$F>B0)gsu`}mO}4m4i~0Az$p@HGc;g*v~8it+pRVHNHbD84~_+Hq*$zeIm%9jq~>}0 z(jleT&9%#I_ABXn*(2u(P4D(i5?(LgQLGqBmM7G;`#u>z2cC&x{r#`j66?M*G`rcU z5|{(2Y^~~St*;2hj-)vpGwj*ed&fsMNOjC>-(Sodue%MP_VzA6tRbBG(ZWL-tVk~2 zX{5fsT0iwr%wUU4Kqv++jd#?0BW3f~6Hw-J{|{Yn9Tw%*eviX2bPXZhD4~ELAVYU3 zNJ+OKB1jC~oe~3x64Kq>JxGakHw@h=o!{ZS=e*~wCocpMeu-Sks5TF@I+kk_fEDcP_}eTB^LHMNw#KhYNIgP}m-t3vfMqnh3>0N@qp$At2Z5skUquEvOu&gn6| z-DBlkiM>~V6}dAW5}*2V4dfltG3BzGy3s0)H1miL<7n-^r)cf68R*uj9?#|(0YtJ! zyC=ujOY1)NjDhx&0u=r~%Q4Cst>t^h`m&1b7JKOLI%W7`zhi{>JUl{SFcWXSlv62T zroX}m&I%wD6dIow&>{f&er809W(>lxBB8xs*mg{mZQFAk+cet0&6<^|=4Y~QWp)TQ z4za^$1l*xRT$-@W6i}plX}?D9)u~iRp6zFbxOVI8HEg>oe7rZWZIOYori;ig-#|>v z=Sc|3uW78eR<$|?#an633qRz(L`Q|q`lfEDz}WFO$TMXICrAZc44?o z2rk981MG5=%id79AE?$PZhnZ z(5v*aF=|Q2kCfkY!EAEt=>_==ElCz9c)a+;M^ZXnyRNZ|Vrf~alei+JpdhZgd|sw5 zIgj_5dV_1k^FP4v{g_j%7-~$!!2j~D=)dVq2Y5_ZF1hjtG8>@yf=I?=j z4`)D#Emz~B6j=ez;y}enSU7XGE3WeMBX-so<@i&idRo?Eh^;YFMG=1M&ho(;=6uBq zQUUx_bQTPdY2fAytoF6euj&qVMd?i4FRbg?#XzTDMn8qjF+TBFR9xEpxLgxRY%j^K zqkk4Pdj0t!3Hm_r#R!IbYcV<-$C(N7+iGrp!4Em!j&%PS0mESdVzp>`D&B1HFx><6 zARILIxB*IR?E90eYYu&NWa*#H#QC0hv7Z~BZJN1pwR&QsE~CeT7k~EaVlT@a+|gR* zx`i&US$(D^8aJt|nm9?(>YgcG7(F%I2KXBYBao7P za*K14c_=|YDJS--QqI)opZ^yV#UC(nYD?ff0uy_gK>_xR0SU-Cb$_a(s&aGN5(rDB8aPb1)codsBg6YT2E4> z(*((;a+z0JDgDia)gvFK_K(LU%BKUd>7xNp8<8-_n97?Wnem5!Z9TE!zSl zeQT?A9uuW2)Cm)ztX)$r=;i*bg!_!ypwS&3&U~7Q-U%sqS_fXR+q*QO+8Xo>4JQ*?L?oRNQ z_QPjABtvfC(NY8BG0O^3m0L1cX|K)CZq^d^ED&f#6`!DXj{YuW|S$);(PNsP@gHhcT@iJ75}+>Afm3$ zhukO8#Qt6Xf{s?T?zC%TLV~j*6hvbZQ@EneYB;|$?cA&N^`&KEUgM9z>Bes+jc-M2 zo<};B?ala@7puW;wgt?wL=qTe0SvM@8PkaKw$qRksV4A_j0DL{|CqbAl&~LN)hM@zR*WZ5G zc_mB$4w3)|yHuYBUF^fJRSz6@^E4`qD}xS{X&0$5sIRVE9JMy<{y^q@;O1r?{Gf~A z&VXw2mqzgBu;}PwL@r&cFG%TO@2@i*cqk!u@1y+IJ&8%ZY^*!k z^RpLL9$#Z4~Qof`dU8+QG1U@jW0T|8T1NMkL|VmTwLo2rF^ zt>w?nFTi1Svla2cB#gQ+)Ru0pJMUtt)xyq>&deqP3pKp?J`}|`g8zJZ$Y%s3=`koY ze?#DxB|%yuWB3VoUva8GtZj4Gk(gR3{lW!VWd>7-L<;DPR4q96Ar%RgedA2*y%)&D z&%~%Wt`?_D8lT8E=Pir)#b$b%YaFjIagNBf|MD=w$Z6-8lY=Mw;?`p5vSc2))Fy?SYJS-|?Hm^e0 zNF-E_j?LLwIVA^fz5^0l`9hm%uVzc{+S`2fL`Oj(VX>TN?8|PZ4nSK)xyRhGpC9jf zdv09EBG~1ZUjkfXy{v^(_j+X(j9AXCD&?)yS7V{4p;pw^y0jT^xWXbqsfVmX(!-2G zQ~<=?x}bJ^M3|f5i`1%STFlDgTftf> z#_X#T4HW9YX4>6OT#Zua46`yd;A#o z-ZSL$B{_*leSSX%^29pK+bbl6J}WmgC_^J8g9*J$>_xb}G>h@OI`5|4D+;3~dzH}W z`bSwsPuow`BqI{h^pu6?n7JS_E!YA$56r{`!Nv<2?uw0co?3g}%!$3pMVqTItwa8F zaXs8Is7R*?>XH94(fcP+h3P)jKZUK1Rfy3K(!tw*n7)X1GB3C@UJ)iHf=D(gplEBug8y#t)L(ad!WAfyNT6!r5t)YDV0LTeLb2=OfgwUZD1EiBqtDm!X^J) zl>{?z&IRlGDA#Goet37@8lSgMyB{Uy%vNs2bT43GqnhZj?Q|^wXO2mNt2e{Cmulcq zpcIy$CSge_;!1Z)dc#~MZLBXZ76p$r) zhv@a?8X7yJZ0{{PrFK+pzBnrrAt@=Mr>5#2@+K z7{*o0Z{}W>`3@l7cP@{ue+z9MfkFB8z(8V?N**ve8*5=@%8q+$iAI#7KM3^ zNlLleNSv!SJ^^rxAx(ljMik$;r1jP!-LmJBj!%@YylBHzWP# zW3^okPY9rom8SFRYeR;i9tm}67g5%@H9~dJw3K)7lQx$!NUHH}-h9=(YZSzW>%jo4 z#3G|ySQn|!b)ZZ0iv7CA-Q&0&HOCYujXziDoB*E=DC-u>UJfGeOhGWe z$0QsvT4hc3Rp#7KfA1Sj$=<9jF;&4e?8pghfaFR_u{gY%(;S(e>rw^lR(3 zi4Bcir*}OI>}l7n{y}pixKpB#;l1x+sZX2|RjKs9AYeZO4xC(k3`x_KJ?C+JmUv-ptXZfg zznH4KF(Kh9R1B4(+6z@LN3=ZAcP;i}Z|&|yupB0eLnQ{rW!8>Jw`=+2kr-sCHbRX! zKh|z&?loWX#;sf9ZA1Y(R6kQ?0#@(KTTNv_eEZbGRRRG}om|s-Yx%Ga8>g!6(SHu+ zKR>rTu+>SUO<4rBnx=!#p=y=k7_}uwpS%j8oR-4EjwNq2&~gCC;zZuKo`4OyubJ$$eS?wsnY{Bsg#)RuCb?0=F6!bV-B^Njv#tnCg0~92 zyRg?>vs4*ZShiiuCfb~u-%Ey1(z`g9e-v_2%ZeH9yiwXaB_!kj0>3-9|E&wy2-iZr68{*n3jjmDeu4hAAw?jJ}`9z5ZI0j zrwt<5aGe9--RelBk?k?b@y_B8i8X*8)f}1np!A2wdBY4hZ}ae@BtBb1RU6G)g%`>R zrJ&a#bJlJ1;v2}cltQoHb=JZMgF7WRRxi8@o@nmg3T(e!U4C^9A~+)cwt1YknRNz6 z#u8_PB#Zg(+`HX+GP6-n^g5}PP9=|`G`h7imE2wD>}ggt|f z_+FX}huwL*DppW%02C}05vVrs@;I!EJe{s?|BBwksoT+GmxMYLA7V7bbn*+ z^+Ya=%VWe8Hd$RxHz@KE{t`o+731zr*2fuzwbp`Q!nRun-tc5xr9~neVTp6>Swhvv z<>fW9IOebdEin0zPX`)8k+uS<6^?D+>C)N+!Itd^g*#HEEv$4#e zyAzcvh*>iOz)9wzJLV=q}c=|}WR`lq2S+4kC>Eb|}YZJZn;9H0Cc z(?xvNCRc!7C=T5tWftO!bCOAR+-MW(t*;ZKsPaK3u@c(}M}EhNi;X?^9kv7$o@1Y< z0t?~DdK9|3!62aqr_d_`{ScCY9$P3vPtROoXtKe`4L-HPR*Vt`6P+oZvHP`5OKo$8 zDn8SaGkEi1zPB!<=F`Pj_9Ok=_N`0FzsfdibOiI zVW~TZA`HD&1;9Y`_VcaFE&JF}&kS!8A0cG+{gT{q1*a^3aj%0q*{Ay^_ra%R-Utrf zW;zzQsbar@8k+iVVm0S01W{5@wfSg>$V!7yB>P?TTk)|qxml_a^i75Gb@e<}CZq&k3wyi%i8tkc!kiT|@vPz6MQjdv>bqHC|mdSbGVP9Gd zGJ!rc6It4Vg-D_l%`sH+aL;l0?ZWYIP%vIw#EJX(>4K6Zqs!POSJQ^deP%>d>Y~Oz zt_t|)ah0K9q!Wuc_cBlHIR?u74-WnZI+$X8KAaHE^3HG}V$mKG27*g&lVefYZ?R#S zbnnVo53fxanjP6@Aw88MA`}=13_qv8Tl(nri^nn%** z$(>Z${hzDq4y*p&o_D;1_Kh^}&n;CVHZWx0G}(L28)#L6lc;uBq*8*g!%t0{<{B;r zQpAcZC2wwaFVfYczgu6s41NcRpkHtl{u`1!Mj#1SPjjXxf)cv;`Shk8$nFIj!D1`0 z5f$7X^N!6dLlVkL14yY!&RIytM?!`?EFM?N{C@vqx0gL+@%zv2MJcZ1UJD0ToG3up z9uk7Vxog!$W6-!kPn{+!>GaporUb|EeA@qkvGCqOImlj(&OH0cqYF?M=-^zXOe2y z)ALG2GuCvD>Z1d61^NFru&jrHZ3$=HA_fKl0_0X=BtjF&GRhWvp6e20uT8=?<=&Me z&hB#Luljg*(l+9!n;G_V*FD=r)8mHTn!NapCVC)rfl^Lsv|Z$|a-aI3ZWgQuo~?K7 zHR8HQvu@vPC!52zP$Gm+t&?qN-Gi(HD7Ht~B*7M$iz3hWb+K97Eh4^W9HdH_A?KiB zOY0vwPn<6CuUK(rVY3n9xLq}gODfX>(6J*Ied)Bj&VTRsq*Qa3Y+EIf!Vi7=cc=;) zCtJrzeMIxuetIB^9!6M zW?IeWlUPOOn@9cHe)AFwalv<+xE;Q9-^ym|x317)`oJNot8anz+H5-6y{Ws8Uc3v< z?qe+X|1qu`_@q`&zth+OlOqys1fO+$C}vmd;4z7kn40`ls`WybE2FQPg*kSHSp6iW zzJY?7W`O@n9J;PkD38SJr``e zHijmbf65)g#d@;i=0$*-A?ZYoy3YFEwK?d=XhVoesyR#oB<59RN8JgZt%+!$X)^V_ z#G7(*x1waQShSC*=I^uNs^cfIL+$M%Jac;2IlgW*d`>pNYM`X6?oX5#TCOVHP$1ZS ztr3uEvO$cOu6V7wed=P}x8?IE@%smNV)8?Ln$MePdjW_~!-vUfrOrkoI}ES8HM*rH zjqt?IhsOVs-J1sLSx{h5zX9`$h)l~a+~#;TxOe~RFX(OdChzP{ZAK?`ANx!_{^gtU z<6C5s%}TA1mia9$ii~aslKoqV8?Y??d2V-%5Q^NVZ$R4ivz2d@90K=>Pa}W)#ufB+ z>5#UZP&mq%;wtEWM-nEr+jYaM`^t3h`Gj@7eek3-^hhpas633=>AE`Oc5gniIAXD} zs6YLyx`|Z7qrbZae=79x5(r@@r);#Umw_`3sMn0k1|KPphBE0}K?4?p!t!~SMIsN1O42o zzv`s7d6I;6^1R#*emAxq{DiYvBqs1@v3;727TvUPRa0?K7u;-|!Hy9^OYw3bbq}!S z49y|{`>)f*nyRdrA#V-affSkz27_-E3BFeI*X_}No3}0wU^sU?_oQ4-T1tU%jk(4C zMVJ1|n-KKkF@jzYO|#Fi5**SX(~cYi5gpe^>DL1MB#`T51kwD(5>UncmA`80_QbZW zvkyV&<*xg+iX-r0U}Uz7a1GfHAVQ46)bxY^WMR_kjy zeFvwPjpoG+pbld>?P>tt{b)Zv{g*C1E4&6HKN{x%u3~EGA%)}B}Tw`S}l?ga_HHQ2dd~QjPdN}@>i&AcU+^x}@ zlix_5dNM~>gaL!ezdk>cFlguZGZFtwApa-F|L_@+blTf*sKYQ6h>$U;gFc?PX3c5$ zKCgG+_O5a3&5-LP>i&^U+x^20*FuR|EPX}%bPO-b{hqc~q?&}J{MolJL|`-rd!uN3 zip=WzMNcYu>ag;A+k6^&UfuOV4KJmB zeK)r;$whw)!7Y1l*nMQG-`t*=X>{!^^t9+U?c&NP;z?rV zX@3fru;HDhyzB2u*q2`-^C<(&2ggz#gJhPhtwr@_9P$e- zHb_gw-XJ6>PPgqtvxa>ssbhW%3-6Z-Fc{FePyvewNx z=cEBaf+DC4S2v^yB|Ott@J!68!%s0y|h0Z;}DL;@{e8Ve*U&1zon(go|^Bd)v zeqABn&Z7+yV}UA!vR2Q#bn`K-Cirb!ipS-nIqui`r+8=1c&IfVajm6WC~-j6|}Dhd!f>O8d+8%FlOl`il{h-I*8WK=+rz=a41dVL@$S>q8x zwWJJEIYaKHw^9eVo^H_DL3$Zc-=Gn!Ay8(h4e-SNpcZ61xJS&Iq*w;(pJgCP*M=ox z58n)iMRd=jGcLl+zInAr)UtGG0cSC(B<({kBI&aqA$%mVbVd2M(MYYS4$aDk@QHR~ z{z5Q>=g~5G7@EY-pdbMn2)2W}C*Q(AJk1_19$#(boKoEn!gMmJh_Xk7tVxopv9eZB zsw)={DsQ0|2R9uh@bk3cy@o3F+~V)!^s33nLdfZv!91ptCx!o3f4hA4(eP;+n-$O? zaOnru1yD#>Jk3eG9GGEGlqEhx$T8($a%b$pqH37Lh{Xm`TF9`hXQ+}Aeo?psk29i; zvwu`ltsK%6*eM@ZfEj5&JY6W!J=qiOdq5E8|BE1$Gj%5J`h3qu8GW6pYreStQLluS zJMF2kADOcrm8Sa_F`6I9s6TfSnmjpMjXRx2y@G9>^`6h#C|{>f9%O<&t|OrQv{%ni z(Y0{43cTEIb!)y4Oxr!(2`zq`c2tq*FCmN0@R6Fe;LdI5?K9pTr*lZ}{N(;A1W)|+ zvlQlkwJ@1HNcctVj&%%>d|*-x3L!d4v?Roi(@OcpcL|Z(p`Sg-n&_VZkTz}XekEol z0)P1!2ir0QQprIy76C+gjm&&ybXpsU>UvI#Rn6DN6?S4?gwKuC zsETbuIDoWq0@Gz_9$%qirMmD$Hkl8@(4#JUJ4MyfsK@yn7BSuiaBIfxBFk+qO`=gm zoPrWzuvS}|EdK-S9Z*K=x+_VwoNphHF8UT!-n2WC{!_db7ds-3UQV4Zq`@Aw1Bw+I zb?yLl`9_d8`KK-AAH=5*2?0dhL!SkJ1a$%cz4Z6L3d)+B?M5Hr-Sy1q$_WyctMP1; ze5(hurmt)XcQ~jwR)?xLyt$6sd%g=2joX&a!ryKPd_%$<7q+r>({hRNiMVW{C%GN{ z4F0}5qCb`ScTN1SDQ_L`*%wQ7`u_5;-T@z*7uQz@Hzgx$NB-Hk%+@z9m+N^X9s6+_ zU4B<)jn2vbJ~Rovv~hC$LH6CX6N`Rw-z!q#FOj34BDYDFaN6HOTJ4N^L#Vu+H_kOO z;lpf_IMPB~I6QBoL3+fQ#uu>;BmZiYXGS6!7=_VrS_Io0Y z$Ig<9A4f1_sn5~J__}e}M)%R>nS8N;Du#Nrd#kyp)M#Nd*f)A~rLO_<)1mxsmRy2O zLZ=$S2`8(BxKdi<=e)%hEm&&{bgvdAgrCReX9Srs)@o#tIxOzBK|UVIjolbc?KxS7 zP#G&UuUq`QIa+&uLL`C}VMd5S9i7(sn zWD4V-Urlh|dkF9sNw4&2ZR@xX;l+*lK)Yz^DoA7!+zfD%DA@9XSJ3#ksX7GLs z*63_G5`RkOzfm0qPDEEwk`dv>f!8A(Uy}np1c!`Q0w3yKX6pH0Vuf{eE(1tdtBr0f zzv{Coj`XN_6DK$83zuU_LH&=TtINnroi2d`K`d7yQo|plI^QaXN}Eh{k`0KRxbU@x z=Us5K6`{*2;$ID}>c0QY~B&N+t!B4xzTyeVwD{E!4uiaM`Z{lnNz(6O!=+++0(bD`%rw zJ%9XJesrChLO}KZL&*_ZEf^7|q-NP>p+07qoL2%QZ*-8cQx^5>?^v$f7P66zXP)K& zzYN*l9bP;|#SKNjr*M4CV(sC22-DHx6p?wiBr?Lr^(1O0kzQ;99Ws)1;eY0>77E(W zIGY#=A`)$>ty>!53q(PV)7a|Y9X|2EYF#PXh?c^QxL+$i00+0BgwU9Rzlh8f9BePBsp=w?+bC5Vwp{5 z7ZJz;21^_#&q?K9}(Y+ymniJ%a0HwTv5K7 zx%r75Mbs)w8J5R2uCz5J-RNE6y*Q%<6v>Yxz+JSL4lJjzG-Ej|a^eMU)*aEyrJ$y? z20Wt=KWeeJgOBhu*Luv?$1e=@>tYSyV=*FBrZjLlkgto%!n&_Le^^T8LJ4|at`%7& zpDj7dK~sOljS_yz36@#HN(eT(TKQxn=sZ=eK*K98F+jg=`a?leI(_B$GJOq7R<&T8 z({2)Xgj0Wz%WkyA$$REantzE?kZ&o5-0lTdpckU7BqKWHVt>R?oM7>RKDkyXiHffh zTXA*h$3gr^rH#8*+WV4QMq`uC4I|)Y+H(7HxhQgdKS@2 zd?~$OiX+SVrjEqZXJ_d%m6#n-UZL#dSdFOHFUVwORh}{65)INz9-3nNm|+aQ8hI2e znVc#&MzWNbKHI*7JCkSMqXuc2^1a>xNiVYgYh&b(<{N@=cgYWu_$EB|S$)yP+l@m) z%bnL-{`D7qv%&-kq|wKLS8vFH)suMXZtTv64p)^LyV((g;#>vNG-5I1KN3A&<}(%Sw{xhe&Qrvw&AI@d7a>e3vi$7IW{%%j3O*YaC4SJ)n`v5tj3?q z*(a3}j_5xBx8nQzhr?wAHE9BKH%kCNg!<@0PF4)rG(MD*)r-^`)Ap6lpxYM?^t9<+ zSqw5O%L-&ku1<@J6%@2Ko};sAy(~5;t(!p|^sY{^9S6p{g>S{y%?r-hRzKNiY!E?a z{$V&T*1#0veep$Z*F{E_t9bk=f4Y^}rjbQXvd*;A_7lyO;AQm0$xrrC$Q4uKyLp0q z!?NhJXT$sYV{4mQsns6;My4LLeMBh`_ale&Gwg)OC>{PdiAqJPa8XJpfq3LsALCPZ zkpxM%geojF(cCcd=y~bT6i*BaVlcEU&pljMKoFsso^BFG#jNlXGoQaZ(?<`?2!^cQ z;9(D+!*}DIt#gZ>6}&lO#G&WJ3HC!wIsn{yL6<=S5648ki4Dwb-(fwxB9r$72k*m< z)Pu;}&K(vl>gIzB{Uy4=Ym+fNj*bmIoMNl=3e=9l= zu473Al&(R>8be?mM2GaLUXO{Z=@^DfH%Qt1PlP_zqpl7`&Z;u3NkKV7F$$BFjC{Td zd9wM4!Wf?H*i#8I%l4e9C|?kZ@)4vLznlf?(Mpb8p)j0;g>>a&hg!2t98HO-{H7y0 z*RGX#1MC8#fz7BGoAuEZ%O2!TiS2?$=0=FqSn=!uG&M)}e<0!_)CZr#d}0g-wVg!5 zM;*i71x-7`8pV2m05`3KDW&_G>^(6oC=Ook(@32QT{~r=+^CeSn{=<^p#EVs*Oe4h zn=JBzg!22jCnU^hpCW4DpP4cHOHUEDu7f@Mzz#JUGZvTXJqyLS_!v3BkuEws?CNI+5sA3kPr{A}77nN#NSMWr^Gb4W3rL2t z!U3>NcMcVxIyIObUt?iBRlp_ex@ixyrE#Pg$ zC0$$Q*bRDdI-n5T`qS6Qvg|&xJjBz_K?6C2ven&X5UZ!k)8>{PEIe|S9LUS3vh-M{ zUl0f$HyNXn9=UhkA$s-$)yd{SWPc@qARnN|fb<5VnVZ2O>u>yq;2~q+A=hzNBs5{a zp6i2-$d}SzOJMCsnD#NV7+>5bRgUs2<_2F+&L2}JU$-}(er!4FC^GTr=Hn3V=GGc; zo|n7`7OlUhnQx=SqUQzfs?od5bvNZsSkJ!H`?V)Nh^G{p3?JU2|Hi!_`-eKp^dN`6 z+Q!{Ol$HQwgOiK$i`ViyPeWOm3DV2MX8=XR;=P}Mr0P08!n8ckM|DCpwisWpRq_6+ zZ1tzW)hlCTob;mJGDC~Z&>HYYpWi_RWcIpB9Z2rJU^C?YMs9q;K6E@Qo?sr@5G!%= zsP$8z)-P%8&ckb$mL*gf13{T}iJUP17>S9ae5davB}%9qWlpUo!mASh+>7uayo*vH z_OsECs6_j{WyH{wA@q~M;N`2>DhfQBQhjl-pD6k3DH?Ov5qN_TN6}-g{+cKzO4eD( z_I|}vGWVDqg{8bT>?p;)+}^MOBskKN)>v$uvMWUZj!$SA&;Iq}loO@>(D;uMs*Vtm zRwckj5j7y9I+O|NSb6o(>S=AhH;e7wqw?D5d{kVFkZfSs)xlbMY?)IRi*wi1cCj_Z ze#|a)ZmT_9=%>AKq&{6Fl!9f5J4xi&JN ze-T~RY^dcl5CIOrB`vy;8%qoGr|5Y6-iYiow@L`7N)hwpGR;h*=X{hw@xy_iE@_fz z9C+@}j3|9Ysoh-~B2S9*l!C=*nO*dNQ7Qk~x1#W1H9#4k{R~qlfJ=YD=NDWV&eI=c z*P*~Jn%0`rPbv$7ZB}d*oLRpRjU%;S`_+~A&6vm$TxL|SFHnJyn4k3_w2WQ6jEZX( zg%T#fAOj55O6D9NEyU*sPIgep9dcK{WO(k=mawA8c9voQ6BgrAbb{k}P6ATrAl_A7 zNJ~!4%Dc;{N6zmT`M9<+T?$?;@SK;O>Hpo>H1I*%SaA{fWFxiIXGCyRRaoo$Jt4oT zu(~{{uR&tP!6kr8)(jW=DuoELSQnvcPHqu!6D`ZhS>^HgoeEg3#1sp{?Hr+wtqpG) zDPinarJ=R+3!QH}ZIrVDyrm(Nv`cehq6+7H-qQ7G_VIYT6ITCZ&@OYd} z=4}!9{VW$~wcwu`xu5N~q3=}hs25&6aUm#(In~3Ww#urSwNcx(q4oHX?bca?SDOqQ z?hY7O6Z^3C>VHam8<5Wwa$-`3Qc~qxq4>aXY__bH z*k27T&4?Xv#lf!1=nUQ6%#BXrRYR@w&1O%41Wp7P9dG`~{{oj=QmKO0lN+Klj$MO$hmW?7`IjP|-3{Pz54SdvgA zdzdT#Td^m~jW!xP=Mke;DDZxdTT1JQRGbDU7Ap3h$S~q(ykNQ#Q)$+ zhXROIz=c}vG=<@i2@_6%%*7Bw)iZT}vyUI8#2tun954SZ6LqXx&~JE3FH5M?@Avbw z+Fjz|b3=G6seZq#^mx39;Ts@F3=E0%3;LJm*zz03chZwB6*dgyzcU-gi2>D`7@4} z29q06?|eD-F}5AcTulvVq%1hT8rRzN7uO-R({fnVeI1^KG}<>hyBVE_UQb9zy+dde zd@ujtLTe8;uS~yAdvE1Y`6wgSQgE^FN$DFs`N{$PY7|YL zbs@(&(SY{MjnIVO7CMuoc(Hc`h|Y*90;LVe(-#o7)>tv(iK+3+N3(qcvuJjv$9@t7 z%b6nNVWjw*S1P?fj*OCEksgVRbl!E&+pyOXs6ioS zI7r1Un&08~RU@kGLD*o@|1K5u&=AR=pf>QM&g=ecxnZ_~!&JSmzP4Sf!wlifK5`sb zar~@K{J}7Q?gW{NmQC+Y`Uuv-Lal@9YuLKjxG^Gk4e2l5*)AO&ML6F9&*c%o-1}v8 zN6JV7gOfrCCD9UVm@yp7tVOxB7MVdx@7Gg3xpEF!0#cRmj-08skGmWstk)qwy>^XX z>jX~*_wR^$g>ymqLm~4EpqMlfl9B%JWMNdPMd;BoxRE%(K2ElSqOCSe38c>Zu?05d zIC(PKX|Y8fqjJ%<`u1eWsIG!g3hMuf+XXr?zF>R1fOf^9pUkRVKJDc(jHpYDfouqi z3$s`)w#17F<00)$oGp)~AaRNb}q4Us9#i!^*nXx0Jkfh5zUdZ6Mlt zwj3n$tbkETI{zy52RZTh?%8dB-ePERj=cIQW=6Rv|4cc>h{kxi)+>UnS;_j30{n-m z-!b%_AVb@{NO^()(;2lMblJXDUr;jc6`F;)5OU%FkFr9 ztQ~yxS?f6@3>3r`%)gr9HiQuK!YF=vVytO=KI>TqJ?cdX4O9|ZGb_A_ zRkB9XrdOMt(3x^Sm@SbuYf1Jo2j`K}5@bybAAum#JqTg3^iXtC=IoNz*^+;8X z*UhRh9b!zr0ey>+ed{%e`8Mvx)=lugLqk~7FdzHK+i6v(B(@*;hcQ|bfcZugGy}u2 ztR-RTGrdGcGFaB{`fSKTretJ--t(YMy3^XHMA1)I6>g-{b;g+$X@>g5A$-e+b_Y9C zx!=W*NRinTXk^uT2#*b!j8Z$-rqpOhdv}Ebp%f#1RRq-cy|?Tkl#YVMvAY4$({-t{ zQI7m$Qbt%94k}7PoqeMFUgqccv@=u;DOg)c_SUyXuA><&e(`=*3+{_2le4ZXfYsKi z#R|qO;#mQ=kOq-A7&PoV5)R8!|J_RcK<{Ccz?&AIY*q5GpdV)&I|nm_RMdre#iZQ_ zulgFtIf9W;_(nlcaQEfGfyW~1)4P4hEGqTw-BO&K%{aAZR7vM^^tuIUppXQ@$t)|N z6U^@py!q))?ql^bggtcAX69q_gc}JsKWhvxz3%*A2pawJDB%84}tXC|wwJOW*w}51)uS35C zaRjuG_VI(yW>kZG8&O#-|HamAgU+yiPdjCuMFH>>)I4U;epWhXKwGpx4th zG@L7A3xJ9mdSizjsEiFtirazlHzkzan}MoJ@wY~{MCKmXHN;A~nCAKSO9KbRc(snX zvbCp&r~5t&2-0W7gz+)(JyU}U`jRxDX(|-h&XJp~X*Z(`Cu1ON49q*nJ9eWnvbAe9 zs3~5i?|{zaMJ2G67~BZ-?Q*@XTbYpfO5W|l6kEXwRP-XX&e_mcktnxOTT%Lu=amZS~b?hw)p!0@ijy-gQsHWMD{Rsf$xfTQcuZb2WSUWaAkA{tcqREhIT~IUb;OrR46M zOu4(?h9dRZk=|{rNYIC|FIhIlqj(>&YW@Xs51Q3}1-YWR-dk^UMJN>@%&X3+{mX?g z^o>OK_=zi0eg_FXcn!3A+S+>0_#X0c>b~xG^mx=l!ElkTVFFc2j>$uTPD9hW$2Jn(gCG0>6B^mn_cj6q0obtlfqNuVLmeSQUcIbFm9& z-xB-UfYK8Ey*JHxLlSG$WgtVtw&+l(KtM!ep0-$YChBlzB#Y(R?oC(PP-zN1tX%1t zGj`Z{Dy=2Cbfkdl3$D_`Vp+UMDmM7j3=BT>0eWU|TonQG8Tt0j5cL^COZpCuD-WmwY?_;lz)pCGvhI*?*k{kiLTO4)$nqOH2;NaFf)n6UfM5lLX&aR@WBI zSWVkF?b9PwSm2KR-ejz<{^OtYh(VO;HiW|m44l%yBsfX9tL=IJA`intC^ zzPZTc@0|3Fqvv#Tc)aWW$tF`qc4T72RS5`e#wmW~_6B53TzkNwXP;k!Uv52wKeXhl zlR_xSlwVl&oB^_i$U!|<(vRp;utXr)=Yc&?XDmLwC8zgY`$)PvDA`L*q{6AzAO1<& z(B0l)GtclFQmdf4lz^k{(Y=kXNG~1jQT1F#XY`B(0pO>xkf~QdjcwvLjduRb(BByP z@0_9M#UU)E`T~eQaec38cffyp>EPKI@b7V&&08OTSidXxU`?o22MZ7`VUz?LZd0FT zJY#@0WQ%7V-WqxJ$8wflYUb1wXh!GMnCSPR{-+aQ9NdNQ8^FGS(qb|rKb|D(^oWQo zR6mu9R65&-9$PQpao^-3+8l>=z@Q5+b$W&giZW--P!b%pz2bH7z z#P!AL7Z+Y@V^6id7O$9Ta*|=keBnKwJg@NGCJ>3Vyzz&K}ZtxAyI>BX-zz9f4 zmE}DRcF^1z!+{{?;9B|Ng;t{ftU>k1Mvziizkr3)iDihIhCqUXgpxV!KPK4%v%|JO z5f-cT|H%3Zs3^DZZ5b&+Dd`UB2I&q#8l)TP?(POj0qGPZhwe^gq`Nz$yPNOj-oN*H zf8Sc_nuR)Z=Dhnn``P>Ky`Q}Ka~u4cJ;SZ~S$?+(Ig`f%V@b{OT<0b`aC%hOJb!P5(RDEv z>7v~o+U9dqs9at;Eat1~F?>dfMoBWFF8y7h!MNre?B*F{mj zbC^5mRXrDMXgi=-Ugkt}wn4gV1UeJoME-6o>eu(k=Sdm<)2a!Jzk*m)j9Y+|N-yIF z^yi?*t&5~_fMQ!cQxz|qXjiEtNK;e`OdE_xB2)wl?EZ0xC{Spjft}N&DuPxaxuTP! zwwxihB~s$Pv%r-UqZqTzNBZGw*ID+eaOcqWUXl7%?d8`8s=ylnnaNKCi@Od474Xf> zpysXVkOGBH(scnVFLQBp&d>UJpWaDMqG$b3ax-33bJ+?IZAZlx*7{a+%b%t^N9l2) zK=<*J>nNrk)MNO-T!M%@IMVSW>8{&RS|!}aoRQsGagWP=j;2fbi1NQGKGU-Qd+lcc zf*Jp%Mol+Um&m>!GA&jZeYY0k#Pb}w6MkC%*4A#py1nD{@{+$c&KP6R8Af?f@Nx%5 z6gap*CWPA3{i!A36#i`m_Do0i4q+G>(VBkFPyHSguB6H$M2@F!tM&;L>>nH?6Js;& zxgIAgJY1I7bb;GT>>q^KP%+{ozo(CpMVZ!1m`f`tZ&#PRR8@o7eBIckmTAy_ga03P zOZp51HJ)#flEVvfpG5uMrAOG9IANS9h<%^_vg=1cLqEbznv+nUT8RAr?5Z_snR63;gIM1ebfsuk|$8Q@~V(Y87_7IvMB{i;>qemvLg8Pxn7C-SYj z@zYj;MCJ9^E9WxLz*DIiq|ZoYbMHpZ!wCz32@?P2RhM6YP|?YvMYddh?7AMzPrY6c z5N@5rkE+`>wb%}zjRfAi-#XmZ0FyM%nvoQsGl2OiYZ4TV;!i_pUJ#q(OMT}17XK0H zo}KR7(^rm@Sl$h4hNChZJfv%Dx!KKN*xDbe`X{AS9w4jw^Z3JI(N^y`i!;69-$G zTjdB(+{VMv3p*$;9l_a3_OxBAW{X!d6bA=34SI!Aw_1_ z^4OfqtOM%srK-Un9Sx#-Mev;B;@I120SO)YE=YwD*&RJa0vyV``Hm*f3kJ}b+H$j* zQ>DHgS0!g25OQ}`918%*;WtA8RG!z~s*3;lT>dB*WtAZg&0b$pZ!!u}ntW=+6)KHN zJ~-SYnAa@ebES{3y3HkDD0qQW0hJeTSY{08{EuAOi18_y{gKz4p9r$;KmFoG%y7+w6RrPV_m{< zho}Hh@s5sgOaBQV|GrKl2d+(Ggd4Nb1EXvhOXtq_nuE%ROE#=P_*m%88?SM089Df? z(@$dVok|STTX)dhJq)+DS_Fak6U$v4LiLAj{;^!v_rz!jnlDlBn8&w5VMJJ61ATbE z*-d;}u~qWW|EYl9}g>aqDvAe$8ZH)(WLG)A6l& z&--x@O2=I*lop{uSe0`%_ft8?zvG_%Js1CcHqeq?FHIySbV5|RSjoo8ePZjDga8L0&AKQgWsQppE%l-Z-|!6)k@|Yy@Dr%i>zcLaYk2gY(hNFSIE78P+D~JJ9WxtX@SN#c@bkHMk)ME43)x+aIwyClUBRtoe=qP7M9XSRivV2| z_A2@s+`U~g&*;k7$W7%=><0|k2chsD#d&pJ6JdWK&K@JfkW#0~s5S}}lPWBJ4bT3; z?GpErP!TDiG_!5q6;c}}UhB|o5wMuk&Y%BM=DNe|@`(VG0eHuZfAj9?4yIX1FD*H~ zLBlOZO4ET8-42xxowu?4y^CE;i7A#%1RO!`zbR0lNf+p_7&Q4{OKqr-#v3&*?MN$a zfycv@6cr{c1`J|h{~pmLHbJN&w_k2=4i}$&PU3jByiwmTM$!crB8^EZo|kGTi-tf- zz?2?Nu3h26Eu58@wS0NT&DZ&mMGs+}H07Yj7X3{0{SE9@o*4&+M zKUuQvJ7_Ufeuq$h4MC(a&CsyMPbEQ!tsBp{^z4N0tA>2`=|<3aOAEr!D*ngrchsi81=mKWpwW*no1XJ9C}WH%+411D z2EXmHWk2$=xSGdIyN~bv4$jH0(Kpy0VB;5mcT zX4yB()Cu}sWSvO}&+`uTsv*E)hMwF^vg=va(}wmFLT&D3P5L2DV9(>@sI?}tqVOU} zsq8x!BX~Fx`9nBWclS_-QU4{nf7S6ylS7r(h>&FQ@L`3|@s6rhyuW^1$NxQ;-G2}% z1!JxR)2hrh)PLtkTnA?)h|i8`D;i6q4VOh zQCMDBg?er4FV)8{vwsrQYK->ZRv(v8mo%c=4d>}Fm<>y$B>cqoLuKFrDrjb4Dp)V) z`#>DiMl~|2t@_idF(q35Do~Qfh6=+&4aE_fZY(p2>BD3{thvk1jj?eePxcEZ(~rql zYfT)3r4WH9qXCb)2-tP==@2S7aU>ct#_}6zmF=9UsLEjE_4)y%!rBR&OXZeLr zpNqm|qXh5|h1X4IO>8mWDmXTOa#TR)qHrtyp}(jM&!Sz==k0xnjFPf?0Ze|#ab+Ts zlZ|W&W*|OrK!zy#ni?k|C*m2Q(-KLK$&a=I#pb*LKtjW$eMIEx+%XM6CqdXjAIT6D z)&go$Yh!zb&XNKg)}m#j@(GACPk}h^2gv)D>!juaP6Th&U;e+u1~9zRuxCG_G#g<0 za#Y0e8P3eiUgGS26l$5YniDvjuTZStjFy6%Jd}grhu^A;xUX@WYu_p61XruPkjg47zQzKMb>yjM_D1oSdyUslJF+A;(Fo}pXfuf(9=CrUxYiXozE zl<%LM#^*x2;_VQxm6E062cyu7Az1W_knnb@Malrgse#U-_*Awz$d4;MDig?eWa$0B z&hkIA3;MC4h{B}Ag%!dLygM}r8s7I)c}lzEbyjq&u9bLCc{g|rNtQGcsLH0L1{7xJ zRlHi3HFL(jXi9Gy{P_C&JhNY`Iv}6fJ1(y{?2pZZx9v|}-sS;8F0p8#p0?P}2B7dA z!1L?j$@u`P3eZ1)Ut~6Rf6R~qI}5w#3hWZMRq=S30j9^<4NBC`M^61cjm#k+$B9sa zglJ2>F*hN8jD`I4|C_q`J5=IK^hN9z-uVWMR{+5v1C39xKoY2>H2!n#PG+4z9jX6M zTwAB|XIF386RiF& zB+M;I@vh^IZV&N+eedF_#i$AAFN!+`SbG7SoMq4xa6F66`0MH|PP9E5vC5V+A_e#3bX{C2=REgm z7c*9UpdIGsjZ@LZU$o!}`v3j~ z45mvP*Sk-a_0yt@x|Z`?)qc(!%8uYJPz-n*6;Y@?iVx~emZ4{ICTZdWKi#>J1I$X? z=_;dikt~e9$z9B`_2Y4cLbmG*L*+sG{ll=A6UG!2eSDi zKuvhXi6ge+g63mO+9~eF2Ig$-zZd(?*ED}#fA`CXeURQCMfRlU%f0Iw0c{=b1;=^z zgdOvLZYll(?6TBnikHDL=3D9W*#kdT_UhNUhKvpc!efcpX)b(x9xVDZ%~d*vM80{X zyby3mn}~0%2XeIa4KP?-5)2b z>BJ+3dV*k$x1W&yio~)1bIuy?g`?Z>O2@-&NEex}H-NFWr%Gp2-!8D9!?IF2FNw6t zR3iT3-Ds2pY{V0QyursK8z)V_C<3Z2&7Rp6wR3}FIVxOEvf7``h)?pm|O!10h1?4@-FfB%8ga2rp z=NEDy!wgAh+{fMq%l1;{pvz^vhkDuY{PB{+^=@v`WD8tBK&Hk3`!&qZp-e;S#Oa*t zavu|4L>(Ia0YTttK!vox)Ah6 zy$D#K*YhM$UlQe7)r`ml>P)uH0^^QfPeqacr##{B1Xj`qPy||D+{sHHk9#T_@Zb3s zCLHK3+i0>QnqWP!gtH^(A?&EC6C5AdMU(1%6fQxRI2$*`^IsNAhBF6nI#}Ub8dxzGZoPj*FV~64+>p9HMHhrw6+X zf22=~;{VwXCK1np)|p^Vrk>N9~nM=F1V2ln zA}+<{F42obXGv%VVLkiD4_bzY=YLI;OM(KSb7BjjiN1Z|kUH9f`aQ@KN@btRvblU{ zg})XYWo*wS6XQ`6jo1!PK}aIHcZSmPro#XhHXG@$dAhvPzNI~&QLCdvJVo38>-&|4 z^@HDey-H0@KybAR&gW+!#%h0u|n3d~?f06fzkm-rw@X6_PT*m*$!2Qk~G-p>bc{N-!C zod{nmhGI>^)$UA&B|+w+FF^B^$&4}K2pE&A`zX2B943bG#q>`OtTL_;NLLO4)(al* zDC?&a@^naM;XQrK#uhU*o%%lLXr-mpK8!v9M8`Gc*`^Sk^kC2lJZ3g!jh6y%d8WV} zT2vGTX5Ti-Sr&NBBAqo5{Hvp*v`?)ZJFF=su9z=bRI*aSg5e0o;Xs(%gx^q|lbS#i zPe1Z0Bj*UjH_Ykxc`Z+aqwPV>S=sF?mt^9mO!}yya1KCQ)l*j|ZTrAVw$xFYghlnu zsBlKm+_&h0L_T!M!&|-cUuZEb8ZsI#W*w2sFzteR`q|jSx*!l)uVfIzl z+Ve;uu>%smIM38pE1*ooF*LCcK1rbSOfRKnvKnbr6FEDaVc|S?F9jB6_*93je*d)v zxYam<_j|^0hUp-MDIRqs08GzISWZX^Ney7%rwWXI#!yQ!vdVLi!AYv~V82$I&Tdib zRBw>}amjP=fVRe<|9##6pj?%VZ(&N<^o5kTvlh4QwpaYB%lIap>Y&8F%K7vSGJh2j zot4^0vo9HLqhjw+go44qWuWM)3SFk!m_}S}_$(0s4Tw*1p^&`$Ux%d`0m>B1mhVdY z`+KVPv@8-SQG*2E>6pkjX;P#?ywxi&9PQIw=D+C2hI(%6A&O+Z_-gCk#-X$yb*i_` z|37Rw`OUCMV;;qhlcW5h1Y73dw*L1{C;7H-lV<8zP8Ae2cCCYN%^cli?X8j|e&yc- z9qYjLl0mGy$SvT2nW~^Y3!G5Hb5W?#YM$~d(r>Lf5D*Xi!(c9xFvDuN@^X(sOn#o= zuIc2KI{Plk{osX}rAiGjKYd8n{r|ADr}r@aN~#o2o& z0}VrNYKlS_Cf&@n=6MX-sSn$4&LaW;t@FjVwS!So`e`)S(>7(@`epkr0k<^tIN|EJ zq>!3Olf1v<)xke%@ZnXM!GXMfsI_@t2O9ZsEzgDVQ^dxrm}t$+a_DFq{T z5kEW^i_+z?<_@z>J(e^U80HC{_s4pn@C^DKC@=J-5ZLvbz)j2LeR(SQP0|6tFt6^g zdJ}m5uch1JNQiyIb1mPR9g=dGk>Xd_Flsr}IodPD8jWVF>%f{oM|~k;C~flhoP#u} z?5+r7{@4sQDG+_SH2OOf`~4^Ddp+MXWjbjcu~%K`V_xVKHbct_I-?|hV>qq^g6KC_ zhC`*f_O%&<+M}tugLCrBRNrHNHlw7ajE^Qdth-~OCh-9)GcJ>J5(VB_Raj3+J`I(G zUw~BEHU=FLLLZ5k_rr2=bR}s4NZq^y!Dq74Mmp<;aWIb`=rPw(eb9V`(!eTOVgK97 z{B!S+^bS;@VtQZoRcdHlP;!ZNz$asNfNIX~Cf;OHm%7XE!eXg%+jP?nImZPE5yrFh!G4G}}60Cp$@P`Z~nrJXT5kGx_ zSw7RNs8UDc?uH|J2~8Ka_6XG4AoN7so9`k!Ym`M0P5ysuv>)9M0l_*mOXnlb91fz5 z0kbYJibieo*n9gYCJG9tS>$#;mx~D#5$jv*je34~HItnwiCvogXn5X409H3iM_)g| z%knie_6uPWGVGUX6d9EbN|V-@33OpUQNr-6<_k`bR+$h(#IdjfN$`O|ga1TEf4}Ib zftt)HAPce)M_k1^SY;6XiaY9c?2M)f)YW=frp?(~GhUmcc}U#80S!@RVH9q%jHlO$q~ip& z$&;Jm-lcc1i?*qjM{gRy=BBjnwZqq;ft@>*+MNYo4_NJ z?wUGbjyH;;ZR!GJ0os&$Cx9%Z^dj3@C?Nk60o`nE!jfDeO8RkXieVPeN`lEv<<0S9 z#ykqPwxQ3g!$|gQaGDDf%dywN`_DJm2EKVgp@f^-tz4jjN5KO{p17(r;oeb=0gGV= zl8e+DNSh6SWG1j0M|ANuY1h{v08l zK9)=ztl}<8uQgJpG5 zw;(3+7)=N3mSN_3H3qDX5_>x-;FW>^7flO$!A|eBwljh|G zqMCbE?tkTS>}%C#ooU1ye?a+8^i{HoPqCOXbDpxeDKaHqxy`vvy(X5hgixk8^UEsh zpC8Or2wJpknj+fD^K!MB4w%o@;QhakTe{e{%twsOs62#|hordPn&Ty*cOc;b{2aF?t@)lMqCt^(%XAwiP`3DOPl{M4}*Io=363&Y9s>>TF$KBfJe_YhE&eu>86Q8;; z4*_QLsh3 zTvrF+?Fu;S_?hHwFQA|)M2Yjd*SL(Vi-+iEb#=UkPG}U*^ZW@UU^wqq0;qS#81v+= z-4SfxqR(G#4rmJ5uH|^>s*8JZHQvk%7S6AIS{d`n82Ya|S{wsPq^R->V(WnSKBIEK z#-j!D-YZB>V4xL!L+OsMo1(;~nnwN3BdZ-kDf-NB3IljNjsAClW?i7JU>x=1>#@MM z$)TE($#{r>o>|^W@5Xyg_EKpiBbPVX6-SwUI0Rqit{M^SUt;tZ)Xu7?GDa0${*os< zG0wM2Z%z3O_gWY^V(fn7Z_5wXFWPns0BOrHotWVYo z$l+3G5$9RH@}Z+iU4z3~s-BonySLqs&0Up{qKp;7g!7Bq)LqxeH^{`d1P*$oPs3^Q z1jc$nokdXdo7dPfB9Z{L#Lq^z&y)rw72Dx?v@^#;3p% zV|xU2HzDxnU(TlKb7LCy;8#9m@wUc*HuUy&w)aM_aWO z(XSj>4g4I&iYsuM;-Qyw2BLea>SC@>$q32Q@yt9uC~`XUU)+#yu-8 zB%lK+CYuxt5F?Zf(39+$6wvy{2qmX#3P;h|dIquW#p^ahi7^BRpLoVeX?I zj#2Q<`P>V`$F&hl-De^E!?m_uu(07d4D;-LD_gw3wpCs6c#)=*4eid*3XjHrbvd7b zqM>m!M8 zJ0kQKP^!cxU5Q?I^q_9tgl9)7U1`!`dGc7-lKUEK_ZW!v42o>8LacNi3|yc;RNf zDWz_j#P`7)u3fIg89gf`FBxTi#WiGCavn@c)_xU)w5+o8sCBxIa8dw#@E^d3j~A50 zL4aZG`36X8oyWGJ^SM8eOBO*S_;Xpt-0`|sREJgjbQbisQlIzi!LJ#uCZV`~1%Mw5 z?f4a-1qka${1a6UHW`1Z$#%g$MXj~YUg%d*W$ou47@3|ilJvJh9YfI_eac==^)?|i za_2o41hlln3T7NijYXvkwCTjdLb_4qTJ%x>(eCb~StgHT)$n2XcDf@HQzO)7;dL%# z$g8Qx6<1f6I5n^oZ1u86gZyXue z8($rUYjA*ZCT+1TuX=^t$Qhc)%vgB+m)t0&75~j_`sl>L7yT-0kCLJV?)e)19CNSC zFPDvskKxxZ{@f@*Juw)x9h~jxFEog6lr}$MxU7K<5zFAA~mZmoU0iWK;PU*`4>e;>?<|V#R zD?oq#;10mcM z+9LSGPhnfp43BI%?PYL~sMR)27w_sLEhO zJDZPC=O*M}e8lLkX};XuqWU>9*;U0098h2p&^#2Evv%w7(z$}dud}p_2NBG*E;dFr zhI7_-MjPE7ztMpO#kC4MKc?Xat>|BUDb5uXGZ_Xxe<5peYd?U%$k{P|#7>WX{{v1@ z8peN&3Ba>+7?o<~LxK`jyz{Vy2^p_8xdW?GoM>pwN;gevx;f!el-b^wi%XToiwW_v zas5%^iBXp*N$&xBdhIsDX$qaEN?c0793J$p>08fmTf@UbRz|)YvBAz5 zt{5=m%@ zI;-)#J#WgmUWx39a<5bOc&2%AiRb=G};w=MNf!&}AUHb9%e{ z^3QdA&KF^z&2i>a?>tjW`>v^}>``Ew+PCzjpE|t>hH`fw8}N{erfsdQg<3drzG`pY z!ggjnw6#r`tAS_Ap-z379x&FDP&g|EIUjXbe})WkkovZNdvW!RGS9dBrn9KU>!8xh z#BN}5^p@Xq3Tf>JsP*&=NSw~iGM_QJ@W1+y+q5vbmW=GNzKY`EX zxj6f9w;z8?sjK$WUwlP=!|9OU?|KavhzVMJqUD8^KmZYU+X7B5g{q zec=hih~@56RKQ990XfoX?LUJjm68{Yg>&7{z176a4<87GP~Em1tl^Dz zx*tJYwGKW@zoAc;E<`pFGb*Y|KsruYk`myk9{E}-E>9w~Yo0I(x$<9DB>|usnrHA? zdcLS$9yH;&N=v2v2!i?hi`K=~HusSaDMweO_>AiB#2}}-sD$&=syWq5ht|{LO)kns zMHWUzPOjP`D|+*(1}bAYnT(MCvc*`K8;!%gFUZAZY@geRCRdD7?ttGu_V*snZOt}5 zs*K=qTi598H*{npq4ZR#1|&TBPLJeG+-+#HC(7Or8Gcn?YtDsoiY_O>0mV^quIfiT zsGQ2csmt#jh$CU51h{#Oq1ARag>133JoC zdbIGyX|r@~97)fr3C3mN14JSKv6a{byO=Uf?K=93hzU&Uu{(#e3qrS~tysgONGww^)`aG}# z6@}fxl~K4I2Tt+F*QwnhW0SmpKd_H9o|#jyAkB}8XS&(L+1>GWFFDi`h(`aVH+jN( z1#I%-9NDRjF|AX@;LpnAlMdIz)@gl(fn#$!%FOw%d@ifa_On!K8QE>N#TqMN^W02F zGNp~_@?S*>G@R6GtJ(|-)4aZ6y>Plz{gF;w)wN{u#ND4X)bA{Q;t-<<`~4)6RB}5A zK0J93iJSc;OaU2Vn3NwuMAlXsM=2>;kZejpud8t|18Y=^7&(sLpB9ny{!74~UKvXY zB9iOUUzgriPQJ5?7uHeU5bbvv4|2SM6+m{^O4F>apX^MXy_y++n!C}f7ee25eHbFUsZQ|ShE;CbRAoIW52)y!B$JjEn zI-BW8C@Dx>sp-rn[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[zbSQNrBA#a`(jE|WB#^*LYD+q60zQAAWdY__5+2;B1j*RA(J*w zb&SxJt3ni;Nnl%bxU&-7jaFuH?Sj)Xk)-LOyiD?KW_;gU9H*Un)V&hafba0q>EGET zwJHIrG|<^1aQazoTnNF^1C@ZBqhfz;ks^Hj9eYbCf9N^FrymT>0|OXWh}cPZpXV(Y0%{l%aVN@P;CC*Byw zQ|H-ayebxIuO$ZA)w5Y|vV)~lfuzoXDijC%#oCc&Q{{Z8;a%dFa0T>p09hy*{v{Y# z;Yhkw-Q%VDoW?gBNMyl>qG>Bv2Lzr>yrqlB1*7t+L56cqgr$zgt9E=gmPnO4!1kN#W8Yg)1 z&cL?7?ihC5hOylbNt#F-g_hABvB>Or+#$Xrw|m|f$^CP6r#j6MPqNBdn^J67d&h$P z{fEX+ZAPrKeP6B**G4iH(tfe`cU`_AB?mgR0JoDNM*LfWh@L1L*@Ns(N$3Vnmjd0R zDq)gEgT~Gn!#5N~uxPPphDj^5c3FoZO%Td?hxhiq}-8HD$!$4R1;WF?fA`1lu z`&7+4goI^74jJ#pLSv^uoOUuaqkCq`SfTKyyN)=&7L3SpoC&}SDIxn?orZ6iF+At$ zWoq9*+s1z|Nz*^-e#|^X>3X> zQ#{JT?$v9h-Uth=mF=WHdgvzrWOh`L3`(OJ>yV7%9ZBBm6pN#5P8=~pE zhX5j15Yy;e)!PqZnlAa#-XPItL*+g`OZKs4YtJ%!7FxuVMay5-Jk+yY%YoJoY1caZ zl6aL&11`@CWl>rz0Qu@b)3E9#3FC0r`ldrMKHio%Nu;jk?=ri#ceWKj?6)U z2uS|;XcH?UWZmoB4uS4__LeaE;4dx({n@j|35OsIx~qCGv^mA0Mt@76-~8X7PM?Ta zL;ns=3u={pW(jghs5L}y=5#Zwj;ConeF~p3Fdt~<+R1Gn+zxrP{=HL;ITM!I%HKAt zUCmal_O~4B@+|!4vb$*f1yu5e6X*M6q1MU)Eop4mZ`+0=C$S2uZL&tbey^D#Je)d` ztQXGmIsN`ivk~R0+7G%UyxAPn1PhkA+5ejba80}PIXmLJ%s)%=2C=Waqgu*<79K98 zOU)z%M~ig}SOq3mYi(Vfr&Q`6$NLsNl~VV2O3xo`fO3QoSw23sZ}4eZ&aBqj7THyX zS~u0iHek&5B^QU;bHt)1kjy3?M0Uleoa%tkBBuWUET%nb%7a{)j)tW8zWm4j7lNr5Zd1H9c#cj518tD807aRQpUH~UY4n%8I`U{ zlPPKZ;2hvFtk(W$@;~X`OGjMUI&rsd+Ws1!k0N@hK{sU|YiR-peHr07>uhznh%&*| zrWJQ$S!ga_kGskk$x5JYS-~mBS?D6NoAC#bfWwFh{7qyP&pHGg`MyKn0{t&64!0mh zTP_kFXJTrwEg~;R67P*`zPMZ!9_XQ~R7d3>rtJtcd3WpWNcsZZ^CwX-Sqye=y5)!m z$MW8V#ch18$bDY(Pj^5TCccIAYIK|(U?O0)+8ZCe_Db{~ws%;sn1r=N8X{Mlf}a@N z`AU^tt8!#9dwZ_8+2%cW*curzvn8XnZs&i?PKIHSlAPQ;&Kum5*BcN=BiNzKa^OKF zrQ^DDpQC|J%c^U2)RAw@aHWC%TjV>7+9S%ksF(>1aFi8lw|Oh>Ka2qVFN?)*YQCpy z&q~P}n;DN7y^1{MEzaG=Bt7#Vm9XSMI``5}yZSgBj#lwO2JTG0OoF(+(~N8c@A;?4 zs9fdK(UB!1(&9lRz^ds#A?S)$*ByEdDs z6dKo`EXETbvwC9dwPve@?mU0onR8i6=u-Ua>@@_cyU3Z5s4JjY?!eitjJ=;bidUWy z@XCc*nqi%AKRUE)R9s{XgxaRZA4S`B$m@0%Gl=3%5r+;>#CX^M`9mM-VaX4GvgDcJ1Gt zhT?V%zU@EaW1!y;lVg<;KML2)?-u?C^*Djx61I!j|7@ri|4@28>Ch(7C76AuK!Uv^ z)cJiwaaJd^wl(T+2xG~|FUW4H^NGQUwPcSfGLY>1;AfvK;R}v~uSPPnysE(y-A^kB z_uTgQATcW$O!ZOxc`q*QN$zo^*Z9)sXRW!Pnl%}_bPV0^>;S~O5<4+vCxt8T@x^Je z!U}A>J%3dnAHEx!#Zy=wfH95wDN+2cwqVfVqkLZq99YFPOv#*td9&#eU6@nA*Pt0K zotF2@BmN_0tIn?13e>we!N~~6 z(HTtHLVa$i)q7qD%B49g&!{e%=JzI-TJ3ByuR5I;D_0qP&ZPdAWxrb;10lLW#8^MV?ENcE#37$`iMhRfuuC7JIFyob*?Q*WExN%2J}E4n z0k8YwPWlx59+8E@Th%5tJFRk!O8;`}A?Z!wOr#gAp&Jhw@xGOxx)YhSjgC1wrk?D` z+3wvyn-~Y8K!jq+`UyVfHR>OgdZ$rK_WU4U9HvM4{t*_*z}9iQCYXLX+j0trgv*yR zfA#uI3ry}|EXzOM0BR0Cd{E-db z{s}5$aT6vR<=UX8pGj87IS0CMOJnw0+r&}46m=2oO3A|)-(mgIyU5M3 z&jmy~=*OV*u6z!g83)J3I$!P8CGGaegUqzn$S3?MV_~eOZ)mfdQ}Yyp@nt=VBP`*c z$2%Sh`~BMD<5G55p%<@2zF^#a!M^JoUX@_B`v~kewinKvmTmu&iF0*&baW^!em>yGqD-lfr%Z&@T1} z5ZfJC0EctZt@DQ?rh@^;sT#9!b&b!VPG@I4SiQFlvG2j7Zug>>tABNv>ZY?dkRzGa zwOT-tw5-tiCt`4<6YVZ%I^Eg{qv^QOJIQ&Il66KQMhw?hA%Wd=&Z07Jo0U~z^n<)? z__%aUeDSSb(1oKbvVam@Z3VX|EIo}v_oG0Xk(4(vFJ&QNKvpk-{B8r4ktRTY;2pex z@R%w=oZd%I&`K1u{_l_zm4ez?PTt@c9)ZM(afWS_aDGHLZ&4FSUF^LY{PkD3kn$Fa zl#c~p`h=r>_76QI-}}Hr?*94xK1Iqn8%%*ed*8r_kpepymY#WczC^%1-?ONKDK-0# zGvX;ofp6nN@0Y^kCueQvlnKNGXeXSrMm`3=!?!Om<`cEE%ZV>2gWkT{PWvBN{{N+p zkRp_-vt0N{C8Ckdm8I6B5K?Qtd9MC#fAUsp!yKKOH-4fRNG$+!@-$buPaOgH-5GR2R-Q{)6< zXp*7mjVS5j56gT)ZgXK6JEhfIPN=7PYz*EE3e;^n;&g2o*G^3#Q|i~^FBV?@x9q^m zD~E|8PLHfet%zSu#A1qbVsB;tt*X^hg?$PPxT@3=@5qm`LH;nz@huqYm_2J)8M+*9 zZd+$_l_PpvppJ5rvvS*^xokG}p=Y1kl!W%CRj}F26B6t1a_wrW^!>B*GjybuGl_=_`&4K#5)H)SExqP=;x#e4H|of#Rllj>$@#4|0f60DR;`4z zgx3br5IVGHV9~W^i}7Adn2+o_qH1$xbVs8v;0mPQMLj{-T{H_J&>_X8Bw2LzI!>47 zLE1WVnT?>rJO5#Y*+qCT*eNChf1U4_b=s zOpD{r1CL%M{vi?5+sym`miE79i-mobV}3&R&rbt-6>LbavebF$VunXUfnW*Z9!S)> z|MDu(En+hCA){{9%&snx|8Pjs8}HFCes|UgDi)N43zRFFx*fcZADpn(dvlrK=fqpe zXR>hFJU7{%84(M;Rqp&Ohbv5(IzPJF`uE7~a|E?U!qk&dvyFbJ1f;Xr!J)XwRHuy5 zb!PxNwu7CkGRA*syhJrX{vOZ~M0y@{r?8bp^XJ1gbq54O)%5$Ws+24Bz$whnTX&UN zWXI2Ee{!0@U_095Y58%6KVp+r&I-GQoT=$QkVDkA{#NWedXL$vkK(=jCpDT?$Aqb( zEG6;;py;IODDx>hDH|EiAbjUU6BLg&&6qi?U)D?I+-1&GO2VB@+d-*H-1~uah`0s9 zBLmMo=?scG88o9k$~3hG#c|Hg!tlkuf56Wkg+v=64U3RQ3}siW3FKcrvTl|XIqFMY zxgjOIm?jy}^*S?Du}K(g$`Ir0DFALDV;tzMs_tZp9842l)JwRiMVdBOTJ+2q(;4j@d?5toR%RKPxHFmCv`j$xmi7p;=sbGGk&n|x~Y8RU6hV%RaL zy$-(koO8Q7E46Bl45GT_;1aP!*|jkGl2-eHZb#pAhAf0ku7BrEf3@heyDVRYc*$SP zZ6OCKSr`1hvJkvN1sU0u(%Kj4+zY@G)lrQHL0Av}jqqhu8B>a2J$?z}-VO-kdB^$) zuBc3~kL;f8Y3kt@2Vgkz=b&XRoeRYK-xHc2gq;tFdN|ssDk~K>YPBRnG>eA@FkkDC z(-^j<08dTCN%VB=S5EWz06spe36mTZAbdnQgs+)@|@zgYh&vGT8kbb?OPf1tmmWYD<|YibR*kAgS9>L`C0mwcXDA6boQ zvnFU@;VE6T(c!q}E6v!C$Q}cxo>y8uWCFo`t#;Rq`)JcwWq%XD?aV#I@2dlFQi~+e z$#xjndm}s@sl8o2~L;`cde8-Mo9EL;ZOMWy@JKOcii^1%h#Oy8po0Sed>xzgR+z-P0nGqShY&P zPY~k(FnC;l^px?7?FPXJNw|q{p|xXJs=n5)m8eCe*IoSX@@ZFLydF*a7iT`bgg1Um z0jFv9_JFik09YA~Vn&u+$0505V5S5ZHDUfbxy6wNzJi|IPt*u3aP^szeRoiNM+Mu( zOYQp)p|wTKn?D8?R_IrIaR+}u?bYt{Qvp|=I~a9 z`sDY>e3q)|4clW*53X(7g^OrDM#3gg*8}k-fc!w^y<`gQRxavlqBwOgGcG&s_V6Y0 zGz#Z#=`8ezT5t%e&V%|yiczH@c>`chA>=<1fw1qV5R_gqM&a9LiqX9aKlW008$&O%(|qR(#o=7DB%-niJ)#h!d^*0x6!utKWCQ= zQ@R_{S=E!r^N7gEPwW@q>N8G9*&}w#IH1`xt-kjrvy3?(e}jVec#aP4l_xKSZn2@b zrp#}i_k$VErNWwSPZC}TBhv?X3%|I z57sjFYThC}y=WuJvSG-@sX!_8i5%%tQhm48Bo`og?}>D$n|#5i4=2jqx3QRM_8O_> zR5w0YBgMEr@7E&2R>+?%|B?f?2CTOsG^iARM+iwU6j#>pm|xSH*REB+3K6%9J2xm! zjwj*oA@BAgAudLX0I}G{We;sDHoV=e&w%gA-@SPI9p)OO>XA^n+;U zZP;&n_=!l2GTR0@i#H<19k%Yb0zAArX(7<+&GzU zzSiWI%BvXk0ws~iOSdeR%1=yS6b~d2ZfI<0J$vDJtsgUw%qzfVrZ>@E(K^L@H%HpI zDh%%FNT*t~b=)r*Dml=@{$(GLvoJ8U_X>enK+5jLZL+BZUC+F^*h~xC>;mRl+3PHt zPL3B%rUl%3k{^DkNis#a&L%fYdzvg)DTiZmmgl%i{>eX2Zzuz_hOfu?F}g`Ra@c%&se z&VAAjZ#B>B+`ginLzNB^e|Buf{>`6FbvEY2f!BG{gJO{bch{ERWu#WUK_Nw(Q4*2@ zvD;;UI+?N-yKa!HY&S{|F@;R+A$ypc2Mma&WC+exYA37N&PtITxDN<@5H#1^iaT+p zopl#m``E_*8#9wlBPN$e21lNG+#=x9D(o9Iu*GvxkZ<5;IB}<}p0IYqHXy)Ll3_0F$9`ss ze1FkE+003zqDL9E{Zm&KjOTpZf)PqSv}_{x%yt~`RA;7%6%h*53>|AIRY^2Z5wgiS zl>4=wx6I)keLag|QYVc-Q=s;5wRb`JVUYdE>-HKrGS%B977)K1#e7a0_}{#RXM^WkP15y@tc`uoKzzFV*#=7Pl(@i@&M4FCC| z2Ve>Srf9Z1c`fh?dB|0M{6TSrhK08KiJXj-`^7qy^W6{Mhs}Dc;uk=iR&*Q1lC725 z2jpV2ofN*V459OamWsaX?r?Sj;GmCLV$;16 z4)ZlA7a|#%Qgmhm+o}Xa;;&_7rEl;GKVIiynsJsCF*L`viM`fvTpV=NCKug76TF!Z znTk4+R1urR`gfF^;WCeV*^w*&@^EOkskPi)w<4(tb>X`%i{2~OcwoP*uxc7iiBZ(d zWx=00-qz0fO_|EQQtUGAv8yFlmsp-GR7(K7+D#Y`4n+9J1r%RmD~?!E_s{;CB?i0Z zw?0t^^Xz8YhFQXvGux>K{O6#WdcKy7x-Ado=Y`KOuLu7kdrTRhJUTpPe=3`o*jPcV zDfzFC55}BHCJrcU^9Ri3y z5!QT5x_clo{RhL-xpq~kZJ8pi!?3IZ?_RC)ckoNclJ`qYe|Yl;7I|2KiE5(V0wTaJ;Zcd{t>BA4>WmFUnCqW>9s=rV-PpJ`G}skt|-bQhUx{-+gRarPB_6U zNPAiDI_Nhppy3AW-J9X8QD-b0-EvL$ zV5snp-(Mu5?0^FB{|0ZJ2*8eneu_p9d6FB|^l)2iY&Tk#GChMMyGg1P=&$?rt~kY| z^KVfugrEoCPK*shJ`8G_V>KwtI;CJKeGg16b;t#@ z|JeGwr0R4h<7?Ky_AKdlHtGQOl-n^qxJE-kXmpDU1Dg_k-Jr(LzYta#k&Yo_%`4#(RFgUkq126S{O5mTg z|0@&?PJRS_t_giA(w2UdTZ>Hp4p-ka7Qy;?1QFiS+$U>p#%~u%s)@R!m~FqxxmIYW z6-radcY)lq%^cpZw@Px`5r6{XNx~S=N7lp`Q0bnDqwP3g(1wA-0^xAmHS=Jrg}r2B znSZ^}r@>P;{l!=|Ouo47yG1>3t46!GC7En_ScW-ak5FaC*7W&j9kb;rUm~#N<8@PD zO1wNhokQ|8-Qy1+BdKLgj&kq;>ece8y0zN8Hw}+bE{#^wVp(is6rk-^Bf3OqnG$#Z zSBgNJHiP~U-dBj(S&BnJd_4OKf-n(tNHoZkIn5GMq*TRrd9BkO?rB4mXw)J2L*V%8 z&)I!KvLpeIX|SU01R$mX?{~RVSKm?-ku)|||A5hYnwoa1x^Yyjl;lBp0w_Lt(I43F z8v(u4S&@yMShj;br26wTfcC8NlNJo2m91;AP0?;?K^Af?5>)*xp`< zmvV5}1tgSVI zb5YXE_$azwP9FEvRXFlv&8#eY3TJ%44k@UI1NL?=2a~MVxOb4*Tn(ctJq(w>MNn^F zIQFtEEs@ElIoc~UDgQ zC}Tv7%04|{X8kHuMBG)>&$^h%Z{Fa$k7vwjL%RRwefyQpwoW@J@tm2KMSsF5Dt~Q2 zkj|h48s+v3^Q@NQv^w1d_Ge`b&ZB?O$A<@PbW=qL9G>tZl#D(BFYD?L@#VkX%&hS9 zsCz*yc=5c#GfSthYbAdVwZQ@h=;65kCVM+-!>^flD<-^PvPjYp`?7&k8kAYph=P)x z5)SFp+2=|^ByK|;GwZrxAeR@8V>U5_WgCyU@rUle8Ox)BjBTW;lIa*J#|`7;BZo*P zo>5t;B5IMIPxDf2a*hDvhid>Yf-LT)>4YaQTE?Pp6al&x9_8_~9rnQQAr{28bh0CZ ztLwYX{I6Z%y*U9q_5W0G1Fc_C+r9_gY5aw)ZDAP+h3td@Jre_>4^TGr{C#XY7E-jz zSg#pPmt~f`yJV(V%7CXge82AOHXJ2?$5>mt&d+q0m>lU5g}I^Y|Kh5lWWM(^%Khd{RmC$1mlQ}u>Dq}j9tea2Z|yfNbDdCy1~7unex z{P4PBQsIE09}N7CXy))6ez`OtQqv7-q{Ag;ilBkZ?iqilu{HOngIMrwoS4zr- z$nHFCn8@M?O6oEG2ExDkc=dG!Q)9vme6iI=>}DUYSX&tr+p8L>3w`&`(Flo`&pF-2 zL>m@f?~(?+x^z$owte;(!~x71jrv0hOxFk!2ai)ty2<**8jbVYQq|4BS1?4m;c>w= zTl4-Zz{i)*W*GZllY>pXC9MVCcGh$cdpnZ3te^mQOB@lL+c7uR$B@u|<1Hlo!P>pf-b zhsAq}3^knP@nmJElyES=d(0?(FB81kSFD-uW$C=)r_NK9VZrBpqg?GsTFC$c0e(XI=&*K<#~SsVOH4$G&+ zHl1jHzTnB=UXqEjZKk8gY|r$^_J?n!(RnwSdnh+=PrTPr($)GI-9@5@E&MwVkb(TP z$)2K$2si^D8b7E#7IuY43w)ZHR05g-Kk4W%ib{xIiPppW&d!Uf%?&m^b#T6KP&@|cGn ztVDLI87opgPBsdKg~9q=LzG^|ji^>$CkNQCijvf)lyo#Ni_RZX@hC>kwjOe44F8FA4j(w?JVH0jU| z;wou1%QN297-%?e33SHZe5FjSITqM|vE(m^u7RVLjHPaT4Bd;vC|sEO)UK6h zp>vFy@hdjC7VUVSMsi@eX{o#&7pT=w+*wM>_q4hTHg-+;O8B^wO2fncVCLx0$%dHP z3?1zaXMZkQ1OMqk2^7pSzSt4T`UDVUT+xzejj1*|)@d zy?|r5O~!>tBy!i{sW)!}@T8AYW0M%5$IQQ*z?r6!kzi*HtU>k!P8AI*NUL>h9SRZHsDu39|9To(e}jWQ-GWANMxxq zh1``HB>`dNyDhBAz<#U|_W z+7&iwtJ%~32!g%W-m23ak-MU*&1|KGmk4#9^S$)ub-II1s%B;p_(#(TFTP>eA@=dK z^mq(4WIydXC)<<2y4r|kwt9j_X*?HCe_pu!5rJ`Y)8#3nX%7emh2h!f%}E0EjD{y~ zROA9i0opPy61R9@+P*mAj&mK3-(roeET1sCoM6xk_8p zj_0=XOO`Ff1p96g_aG{I-e;&zKpjwO(5IoG9$uM_?=@dMd-CVGK`Tv4?}H;GEbGq7 zh;YHYHoG<&Il7fE_e7fREP$B#wN2_7@m#WFovWh}tQsV|y&-vS_UCq7%viITLMaF$ zmG*_Ysdl@xLy&<2%jMt_UC}302FYg{b%uN4}Nn4e0j%qx=*6 z`*>vz1m-3VzUKRZ8glW%I0Ts1l00jfSa;jW=~@Vy)9F-WZqG*5_L)98k@(5{wcp$a z1y>P}M*7xN?vX|K*4@QoxLK*X7;JK9Ml1^IoL_Q0C1yjA(|RnT@O)`Q;c*QIt0B?w z*#^LZa2gD@nTtR1UXmrP=zLc`PmZWz`_p5E*6DC^INh+hETU)&WIZWeqFF}YBZt|# z0B<$U3%tLi{D!z|N~9~%no;B3RQ@?#e>SLyf*Z^B>E>>Ey%)x);U?Wi`>IC?4nn)1 z*o$b=NSb13VI%LLHoia|H>y*VCh83y_4j<&C*+u48*YU*VS)Si30|8#{K|D~dZHZO zk(<-sCz>}fMR~Gj#=W-fZKq$ME1!$A#p6V8Z|jRp!EW0f{>s9zP3?-n^%d9QWMJ|F zW><5PgU^#6Ed>4>xSQ|ThLfK>Q&mp1*Y1~_z8hLd737Z7b@vgPa`i);oU5#;C{&>@ z7%whf`)lpdFi$aSyB;L)^oZy(h?<|><%0xIS23~q_}0j%l5@PpkoU4&z?AE7t7BqO zQNNKIP4q0%fj65?0HV~=!VprZ=f3+OuPyz-GIdcUZmWso;HnJD4X*`#YVBzLjG^&& zfeFT~B|iTO-nvhAw2l6BB9Ht<5wQ`*W$Hg4KnYG5`z9SGRHOvN&k?T?Doq~?j*Y$X z0+Ymi{^lNB7eE=Vj(2dVYD0*2fynNA^F8#^;wonU{UiULXTUf_&8z5QV7WKXi0L;+ z)TCmu#CUzY@1is652U@P50k8qYm%@$$rp-}ia~t^VcavviQjaLOS~$oa|Ow*uYit; zoT@E-eQxfypc@8|a#i%S3L@zB5)D|o{)e<$&Q9(vHvae*uL2%0?W%F|&?=e(*`L3@ zo@%NuhwpBSEM1O0yeHM18h@I!aUy94l6)6oxrm}%htKt13P-{YfW{O2y6yQ*dYbuG ziCfjCG><}5Aokkmi)dRQna|D^WLw1iL##x2(9WMaQ~%u9H+#`91NyPYfb`uG3A?`u zM@wlI+<8D5ltn zjpgfMdkIamTtndi@)uLA@@nnu-<$uQbqA7*1WTMdJh@4DQ7omTnaVycO86CRD9y{| zBX&OVMz8d%)P#2q~%Y1^7G4gV^AvzTfP=> zYocrIWT29JGP;LbMklizD|}xp80WB*_?iX{##x-0Wmr%^buEYDFNyQFU=%QC?43Jv ze$s+tldKAB92(TV&h$;ckyFz!^9<-xr8>_6huk^~r~g}$#`=bu0V&01K0{w0(cS$N zQrWCW?I82HZ7X(QEMc}^Ue7-Llp?feJSl0*L+jGDviMECj@u`tUYlRz?7JomxqJ5Ws=Vf|vNwy4}`-NlVTmM{Qzm6L<$8jy;V ziI&zj$Umo^dRjBzc?m2`0%$1@wOJZ6FsO}(;c;seks@WJh;K$$14G;h+QCMBK$3Uu z&OjnkUx*c<(jxo280hl$23EJkc$@$@9({;C7@=M484?p7tWGCYXCl;P#^x;}%8k2C ze?JP5(zAw)4mY(n(+Lm4>Ed~3ONI;&UsIiSP;T+ogiIUedE*fyarz6twkcGjrWm=I zqN1=;V`KMYXGW+Qrbo}4vCjL*@ao7l< zL`+tlVR=y1XH)+_dDZxVEStsce5WcQ^aFp^tW8wM>I*Tvh{o%9oZ7LCv#n~AMc$Cu zW=mWo^!T_}I75iO{&nRm#@k7jQMZya#=kutP3~Ql{9;kF39CI^rD&HwK>_qog$p8N zD>$*3ltpCzyQ6ZQ2Lf#2;0Ph(iL{YNQK_ddGq`I;0YQ1x`oI zmz)r6PMPg!i4rB#TP)3P)3bO(+Tn3*j;;}yG=-Zk%@ z2r&AvORz*j3KGX=oo3>aaz*c5k3fNc^hk5GvJHd1 zCM(VTW2~^HfVtD%8uy_{lt8ljsfq~Jyy;0JSKcfOGJ!zd`kKFYrYQc^Y5Thi-lhml6=uEp^vN5OjXzz(`?jI8cJ2sBBHI>8p%uy>ODkV2|abs^q>tv z;qeIcSLMUv{Iokw%}G(Z^`*5Wr`RO*$v_Fsfv2wL*A1~pz%Xn{@J2`Y0;N`H+}r-N zb#~a4#M6UEo)X6Rjaxa-y1u;kEpRiZ9l}o?BD5}Dtc`<4R)i|UnitpyB1}|I`vOG` zZU~M-$;e~ho(4&gE5k;OsGkZi;R5>($*?2(Z$P768Un@U0m&C4bmOZ@TQ7(S!A{*z ztsP)WcaAi0(#L>qSaDfZE0(vmo_(*zpPMQy0&DII8-wxDVVJcFx)vVx+I1*!uTUt= z8$G|@$g;Ejhm*ut;^bZN{yzBCc%1ZW!e@Vnk!PF9#`N;L{tbA%?q zZxqWc!|uqH&M@u8XX_3fkprtaaa1`1L21mi+v8DZB`ze|fG%DNRrx$*r@77Uy+rhW%HR{^&Vs?Wl3L(kTr+OQc{SlHnibF#R?gY`dl2BD+b=Q!8+G0<==kVr7Qt12wXNUk+L%` z>P(n#ua`HHc(tpw!Dm((k0fjlvRO^PXhsYb!bpHU7y6;_H8p|adB@kL^{3fyk<-p@ zvDw~@OBl;VlXN8MY-(n4OFN3}t$gbh^2tua*Ey*_G~Nn92ze7*N)Lqg$|f`0xOk?L z^;H9M(b$i(1?pB3(;xnK+JHvaO-`Hqr_n*evUL5d1GxQe>%kSgCIqVoNyA73Mg6~` z`7$Sv7;-lSD@%cS;FwMbi+d+UIaVNYPUT`(?TMYL;<4QVt3U*D6r%GE-q6#;-DNzu zg=Zu3XRd(#?bBl^CNOsBss{{w$O_49UUu&!rot~-q zgz1O;usLA{8&-MEkk->NzRhEymAhCTytTBtt6ryMsHUFEyAFZNGZ`L!XIHG4|7rFY zaecLu@(qsU!*>|*JB_`Y-RA2+uiXQaOoO-4c4>YL5jvjvnqld-{1rpol*vO$#kHhY zMA4_?e*6i^9%+`{j_}#l8;rPPYZ`jNpLX~+1Twb=(CzZSsLE(Qi#)zv%ZZ9}2r>;& z+GqydUZdbK1oQ`KjQW&@_bf8J)1L{U47=GJ)pl_=X55J;mDGP?nZ(vq_h9`Lrb2?$~$x`x5~+4wSgMvZRg)8y$4j?u<`InK=&Gu z0?tUAH|z@75lf%!BKrD1-@_{A+|l$%IDW@(Ocm2vDc!dvvy8|5qxbNjMLjK%&AK!2 zxji!$U0|YYmkIl{LVW4-zZyMyde*Ur$B}`CFK-{o&i2La0zQfkA?4$d(u0x%n;v{G zOgA_!+g{P&caS%^P%Xwusi%@*sQ5TC+XUMqBM?^PmtJIBtG&)nC^YAmv6iVyu$0eL zC~}Zhx)h?v@p2eeL<)p?K$>4w2ciw>lQKg)6;9Q#!rmeFMl=jVe9HaU6gP7H=*{0%-9sbeA@TS zZJN%Tu1^`h{u>Hw_$FhU_`ASB>B$e3&<{YxUC}dC-jh|-3SRup1n5rBPib}5G;}iA z4_4qE33)oeCyr1Y2gqU%ZJHHINK}q_Tt4yoa=(^vHxhqkR>_ix)V}pq0r~PLgZ=qs zf?;$%E|_7gOP={`Qq*#@L<}1XEE*$#O_k3k>`mW9pPG9aHFyVP^z{k(tu&vTHz)s? zY9cv>~qbd|vm_g6Ztjhr?YOM{Ek05m0CI+i;~^cE zh`cpT*MWaCN{Fe$^&!k!+gfe+bSp4}PC`A0#G~`(5Yw_^G|(OHdjnfIjrSgsMI8{ zBp^6Iz2X73J5HKgI%@d;-|sJ^>kquBPWf~aw24Zgg58y;CQTcwL}RxrJ)7aP&wJGw zW-$kV8emTHTLe*K+KFv~Stx6}8rVkjV{n41W?$Lq-dFM1R14Oty+eZJN!EVp>d$xk zrmdB7rIt^aWAVROc8T33Pma}cWqK=(|BAUg*`)-$jc!Cwr3f-eULS3HbNjr9E@Y?5 zLTzjtTztd+(^Nvkm1rB7`fK_MKvLlu?$7@@?*!S$G!AAT0i&8AW(& z+$QmirO~E(P1sy31#O!>InS6QMkZt@nTD2z(XLW^8{9JtaZ}2azbzLKQ{G!*IJiyn z=B;>E3)?%7iGvn$KHAqgrvX_ZEH-29NVn4}3!~-PVzGiz@@rLvPc7Z0q;wS0fw~J* zr_FTAEMtxrIT^dnxraIn%1lo~QS>lYZh1BGGCc}7uDmS^Wwqo>!7ZS?`#iV`7!#jx zs{Uv;@}GG^o_#GC7o|;|WE`lvSv}NNQxb=q9R4sKFEtcQ#*oWEbK;lTq1#w+Ysxe! zZgq3&vnWJqX{fXiKoWGB{RmB-=xN{#KgLjhyVr7*#}f+ z(^v(^J#G`iUEixg`5k-fSs#z9PO89+Ny}aCx0EY4D>q|5daQ=OUN5#Bc{7+E*2FLr zAe!@6X>#kW3AzastJOj=f>&}&XAW3_2viD0puh8HR=ocdswvlR2;ta4MG}N0F@WKSAy)YtkS4aHMJ9^cnV- zRHA6s_pW~hmuiW4Lrpl$*S^&Zd4h8|HED|0ey901lQpFkCULMmP2?J~rAmhfy3!Yb z0&xzXKmAXEbp5HiGf8}NWZPS+y;Pl3F-}v1`p`Xo#r7zoKGzIxPF&O67Z7^957b?c z*-Yar=M3yHU{ z6Rfog`as{kP7H~@p?=)d#f{P)A^wpDJR@S)d1XIWT2vrnxrl5r-Wz&hnQ=&c|FMP) zOa6B)y+Y0&XpMP-7|xurr=#&JKb~%B;^?lMztOG|YQ2nO<7_-|Rj85^ zpM|NX3H~arc44oj1+gMBSr0^B-DlLTap1DO_~3EJBbN!XC$T1%5GdK7p=O%*fdpH4 z4gLCVKkZpWhk%+=1044l;X3T!r|7>#JY1+!A|WFJVv}WTT6ZDhdtJ^V5@w>!{AJ)d zFsRtJ4K1+E)ewlv?(U@uT6qhp`j|E@!P*JPT= z&7{{)VD;h(RPFTLJ6|~9?8UTIKBKX^KPW@drF&s6uxKvf;#VwpduRy&h&k*d-W(uc99wA&IAii^>dC*AUpCPEPI2>8O0~fm;MRMOvt;;uM zG!w}SsJfp<{=&W}_}c=`irxpkojdQTWfmMeS~v}cssJ^mRFFF8aoS#U&jj3RP*dXC zgx8Ap_KJ{<$y-Y?1FH~FS6oKsC%Fqwd?cnz20Fsm2#(D*n{Qo==lReBRAQ&6$E>4% zyLXjGQb~wzyGF$}X8Vmg$ZhA3YENzmFickRel|4SE0tjOT{i~X*p|-JAjqx^Z{Ak< zM7jZar|=RBJb^2)qx>&wH3K;TTkm>Zx!vy;Usy1c8P`%kFFA|u+a-UHPHfSoo8@nx zMhChCYYoLjY`I^0HeKou2vfeTPr!Y@FnEFeOh$;{MN{v8y#ONe{J9x@wKYq1)B5`C zp-xX*e7Ol@O08VqO(=JXeDDXcGi(1%Btvh(A4m0(65w4>7yf$_)F(nKA@w*C_C~|` zeEVl{nyG7zT5(Cwt4@Lal#Y88!vhbvRP9Xv?Y!78-k60G9RB;KOF@+xiLosr33>1O zfi{kl5y7=SKLd*jFLruS{X`Km(19doV7X7)8}i)Z!(JkpsgM84wCXDRgRgo|w^VuL ziH=znL$bU^LE0PM6q8Pp?4ULj=n*GD8&_t=_IXjSCAS~cGCzD;6|-EQyW*eg5p7k= zKbB?d?L9YX&9Z9Olu2Ee&nD|M7ZS@>MJJ$bbW^XiFR5C~Qan*e*G;H-gec(cYZl2PLzY3CMd@gBBpknAx z1?ihUM~5F(TMUL5$lGk*fA_kjf} zid{NSYba2RnPw--`~%1#SdKg~LZw=f+FH}k{~FDd^y^jqL?E{vWSoi^Z8F9`KQLAj z7fTdg02}Jy$?&BSIN@?6#b6n|gEOUgaCsqoc0pxIqweDk3id;;* zvo<;#FYU(a`KvRKUW%DpmOYeoanx#{M6OER`HzmW<7kPAWm-2hEc%lH1%|&m?tw{Z z%lY;!^LsX29$8^qlJ@=sB{sY{ic^8PkTyj`e$k4b;UNjS+TO;Zw4AVxN}Mk5bHZOL z;`9^%WPabueA$$@B+4*WUL!4oM6@OGB)0zfjJ|xB>9;N0()i}%pRz-Duc9HMbd%xAFMy20KuoyM&~qjCX_F(dD=^r0)}h8INJT9N_cNaq z9rYHAekqo>r|TFO!Q45tS)Sk^pT2tp>(cRD3|*&?X9rS;myTxcP`rotKUP%}F&RT?(#>za!scz;(cNIc<|t62Zbr z!&m;>W~wUyZQ{M8;iMQsBB3c*+7?mx0I|Jo^uVP`{1Ttg%Iq$@nU>UgceqE_8%~0# z8(n5?^#qW+-6-HS<+NfTqW4V3BmZG^H(Ci-f+sc) z^o=y)OFq`Y0!3p*x}Lk0fqhLW&a_g(jQ~x5-Dd_(vubIBa>rH?#cIklJ|3rMXMz<3 zI=BlZHd{uGhFQx#L)e40@{lwMfp`8A=u0XcHuU%5A+b}r3u=Mrb)fQ=6k;LH;UrafmFxTQ2F3%7Cb48PM z8u_Z_`tIUM7+tgiHp1()`ej2c+Ppci&8!gktY=^vPmYnCo30!bVkfI|>)9k(&9m zdiOHaAu{#n2*7@E0bXP&=pID^KkxvY1Ijn~W1`}+8p)|#QAASj>j4WkCODSN-)k7c zy*@PZ{7e9ksU`}Y)l51wx@ePB$7o(7v$eo= zM4{{3BwSVCo0Clw=4e<hdP9ZcWmaxQ@9Kdt3Oic*G;BeuDKFYA zeOcGKwm36-+d?8jH1Kj*0O{T3CHB_1P+%9`!&)hRex|ONoSFK#Fxq)*oh2JhO$c*= z>8wsl8#@el<<+oa?s7>5a7?GL+*Rpq;ykFS4uHY@9iLKFu6&wuSNf;7JRkk5*m&O# z>QrG|XkLYZZMC)9BQ!Z*otebl!bAo(#y*AEd+r3wWeY86sYYd4w>Djca;ng0jufEe zaC*>#%Zc0_0tVbq>vpv!i0)}jMxff*X}B_d@uu4(W)0TbdPV1AZSulwaKOG#W2X&I zyT_k4{d{?Xa)NSdXy5lUR9rQkb$RudpF0)6=9&S(1+Uq2n&)AExuR{Pk)FJT2{!EI z*=ys`hJt$IN@WhYxH10SU=BpA?(`Ol>sK?|!Z@lp4Owhxf{u1FG~lh?)hTmm(e$oH z>tZI?u`-+B`+G%RazZ@L-P!M3Uqc9RWf4qN}u#R5C4mMFezd=JZ6Mv zJhPQhG-`neF@NPO3%NCA04VWuz$8Gn-PHe}VH+<-c1+KAIb&;9^$MZp(~=4vHkWD+ z(;lbE{J?-^HK}(hLzB&6BDSc)pX#{&PuBDRr2;?&DtjzM_L2~GgDX(V$iBkTE)p()Y@A8*_k|V zB!>UXyKkz%o%k?1?hfj1iNWuGzG%r9L4+5@*x?tnjy*Fc!-ouslQMnsw;z(RBYrdq zgL&q~xw1(5o6XKE2e*u@V$HyJyGho6UwKq&*5;bvaLp9cVRfIiawnKvu8P|Hx26+P zlWWMrSL^Is^2VeuxDj1N1#Ho&$5h>WUnRN^c;*G{S|ax#X4WTv^Ycw-;sVIijAfJ$ zkLKz4-yxuervrHaY}T|0NP*!esLYHz(&MWl0^(k0wy4+VHYEzA$-QC^&A!Nagg`#j zBHMIZX=?3N(QJ~wQ$!z~Wl*b5siA(uqm?AmkQc_#Pv$idKJ>6N9vuJg(!>G_pUQW) za=oLNr;~bRbeZCIr43H`)j~G-pFCz}x||+NlK#fEQwedkF0M(p_C%H+P=@aV54c-0 z{OLkRdQZs!p&Z~#3%^$IdP)6?j#F_j^y0J5ksUOt>Zfn)4Dn5cskCkTMsg@uy?d@v zyNF}qcNN~=5T}F0f10d6dVKgF9YST-f{?a6GKqO7ImFS}YUw<1u)irVw>TDPn0cfl zkaqJ0$*UEc^KW)Bg@yBph51<{pUK#u>Ah-^X#xoGd(Zhly}$)N&y3@9SY=Qf$4>6?0-#nJAa%O%;egS(cL5ZF@rQlcKg;vrD<}{h6Vfi5v`- zzYTT!0NA0vH2kWf2V}zjki`*m``=uLZ~D+mFcxY-S(69Wnevvpaz>lxQ?zTU0?6LP zR(|FuIAFx4OePD%JhSDZpC?*H(Q9FY!T->5x{S5;Bm8qX=#ndRSUYLVOCk5 zp{xCo{OXr=7hA8ZDwpMfZ(Xf_wP(E_W#wP90gK+bX>(5Nta8Kclhh2`zCPb?rS}Ks zmHJ2>rA02J^qz?V;MfIPKAEa`B@ug1)y>iSitV&Byt?qE@l0E;fXjl9xZzHlf}L3b_#Q)UY9IFM856@@*(eDEowhu z9^m-C7z&jUKDcfwD*wP1vJuJgpCkEeqa-J5-7`6f?u{9_DGLc>GHR^4_?ryTo$@K$ z%_SAQTBpJvKTbL+W{(tEj`XnqcWzyBRCNc%)0nye6$|@Q1Rt;SU1s9G{^Ecc3MEHl zM#5%_TjWDLMNdBe)e^;1RNI<8m0SahKZzxRmXqDDv2+5E3{5TMLZSbn6W!}2Ebgq4 zx*R+yPx(YL-T}4i!44XL&JwwxXI78RHG*RNnO_#Z;TAt_jJTp#SFQEjjx5gYTAAHV zI=Qw)4}imBxK#CN)2-;KY5Hjqd($}o?*7+T+rqrx7?=@ zcjb8Vn=bcyKJU(`kvz7vP&ExWwU$(n`eSWiS9xy$u8G6h-ReP8aCv?Rf0!m?HMWUS zttiY72U)U*ag|b#Q=l3&y^Fq!*c~_&d?JhV z&JEy#9)g2254}D5DVV-%-&=4<$Qf6%s(2AMqv;Pxp0nmNRq*S&^7+D9W=gMI%+z2k zuYhlR4-P9O1#s=rm8;+X3ls#1{L$2jl5aA;mjg98G7Nz$1HWZ@Hzh5_KhHFAqeO6} z2OZ0Tf~Covpmm3psRT&ed@i1r&?I!bDlfIAveIbdUP^v&m+6DG$d~^CaT${bWP?OFDHG_@l`;_Q<~mH z{&$z7M~$mWE1~7hLl|a!)kC_kDd!)_Exne}zCL+9rQ+Me&Rd9XHy+atTcq$Q!63z& zEwG~RwpRdq7P?QlJ*Z)>oWA@txhPYtIP|vsrd)@fX>-bAV!gthAGvKO zKKu#~t^^66ALDq9&v0JRef;`*d#aFq6C5HjYLnlj`aW8g*pzvAynN03N z290V0uhiBckjIb-Ts60|1Epmg@G`U9=7Ab`xfhTJU<23wUkf-?U!>N$zWr3a=GEiV zbR8_$B7oXa)!FG1I%&IiWd&@?rxi9_^Str&BCAn^7NYxMt?>1}7cuGCgG**{Mi3c7 zA?J3(`MM~eP)4m@p~!E;zO`&$&mvQ{lA`bv%i5}AoIn!Da@lM#WAmj z5uWwX8I7jR$Qa49D~hA@Igf`KHcpo@#gfgdT@)tSoQcc3U_6916Dxy0Dbh`v5735d{Gfuc2QRpJWl)JXPu2uK zj}lU`-he>KXI0;P3zpV6xx86=l?eQel~=&OumN1VRj8%SFON|d%2%N8sZPWiCs$)! zR0L2d_7y}XnR>8*n3rx8`Pp4-=T&?`xz=VKy>c>|^{HwK4c{m%A>8Xv;bP5#-)w&) zTm;Gh@6A30<8{<`#l%9lmj;X4EfkCx(*aBtT$%B*RM9JTHNjiF>IAFj`nxUOWj;d| z5lK*|A3Mgl;+;2|eu#Y7wJW;+Vuo9Nn`CoqjcQln&$Op^rddCGUTa<*akmZcb848Mx?r!CqsU8CnbQ>6^vzh^agUI3X19b!-C z12kZ9C!@Be03Ui1XM$1<%1VJB3aeMHan2YAvujxn z-s-N}z(mZ*awYTZe9Z}mXcWA_d3=hUPH_$JDD4A;a)s>a0@1-WbE>I0++ zLl!^3QG#g!TM`Vbp|Bsmclg;vw{7&c0C@ z^6RtMcQu*95Fr2XY-%QqNhaR1)b$AX-(FK;nc%9$R_xqu>47SOsu*e;l*e8*Nr}bL_=k)uSihNGM7pOMC96#j*kJK7X`(L03F)TqyD2@-FCHg2XunW9QN>EbGHCKb!*n6jd zAWWIF*_0c9-6G#GTy1sI;VCw8hsbN$R}SiD%*OSq{;iYqe^ucSd4;~V0neDD@qpm( z5Xb-QL0Wjs0J{JVQ)dJRulV`^6KvFuU_Yd(&O`0BFn~?yA&5dPZ>>xRYR0P$YjK2d zddbG2eodvAk^ZKRAJWDhX(wPWDbD`k%eGK-w_(HDa>Hu1-?z~o9^CK^q#H+d&7>&}w%fvxGEVizB+-D#1>I@{>c&Sb=VuXc4?a zJdfT`w*-F#GF{TfvHgPac42Sc=UU_GS(+FgPM91RQPC@*Q&6ja9IqkGk|u@d5D$40 z)@5-YQ4wSg=oV2lNM!BO;%Kf05yXvb-5 zV$<1L24a`2ML!VCW~kME_FBi}x#W~MpsV^t08$Qz?tM0XNHln~AKSi9Q@6{?z*}tf zSZPGJG1|TCf3Gmf3-%!gV&i+1p*=hy)`j8FvLVFlyFKrG2UY7?CDGv;3w~G6E4o4Y zSFjx%cu~~DdE2cc@V!)w`@2#M&2WMSo}iponsuyyGfrlR-Cf`LInT(9Jl!b6M{%6h zB1IvnX{Wswg)9e9BvUq>%<~X1QJdMWm`kSo=J-}werHN9Vn?BFssq?k~TSO-jX{|*45MElN$3+cmi*M#fzKUkKylE+*4obXq-7!y;$_b>&+FAGl z!fLJ!p(O2!IY}NZ#@XLz1!hLsdHE`-g@+K?kV*APIa9g)Pw$EP;3Vf|6x z;Abz-p78_;5M31k1LW9qt^t0ByWx0LfVWklE3KqgJh{zDR#VVlfe@9VR420+F7D7( z0i?$7BRpefRj3lnifN=^?KcF{bb$u&Gc_H{(|3H|)ub3=Kj{C{?MDa(ga=C@Vy8tj zMH*Q;mC%8Z(?!)T`pgyzUSk*h*uS2JKAJftho=j3K^gt3B9Fh@_njYLV&fR?(0_%E zoLUSUU57=)F4c@oRr^ZjBZp#uxUnLS3Ya5DMmux zi0C0|dZRC}!iT*>?{8SfT{Z2}^Z9b4Js_L5 z`}|cxMo|InFE|11g1R_iOJ)**8BT6F<=5w(;_2}(YU3@k_ zX~i8ls=Lv=$1im6thWKoc4CQSfoay`484b&mWmlbMj6sz58SxkpMH3Jn*vGE$5_X$0#~t0 z#o7;y7L;>WlKv5O1Qi!lM~0aSTtX&I8VP99pLB*B`#}Y|P5+uqO%dKW=^9OH3N2IVBUB(L(wUL(P3uf7i(%o8~=rmSW zn!pLpq^IZoQf!*!@{cc2{{faRr8ZA3Oq3_esO@}wx3Ka|n!@mlIH0#D`i%qv;lkI_<{)mVfwJ7gdE3PyF6AYgpclUt0O9wEIx_yxe$_Lp@ z;!18AapWke+7>JvYC96e_I$5^QwyrfhtskX@}hbxigZ^J{Lxt@`du{$WUZR@S-C48 zwddziP0(tb*_s#Q4ewrgR?tlG{lh21!Y`1GMK}m#%^2E7%sB%yx^7kl2{&k^6K524vYbeT7TY2H+U$?F(2vUo`L0l(YQ#9~;$U4bY5(vv z+GID%2vCLDO-<&Z@`6fJ!2)U8z{ua403XsP#&(!azVc@mlg+hB%xUc-(4jr7lMH!k zx*h3z+ed>xtPyz^{qceH2RKBOb_^l+@U1T#S;7);qbqYhPnQfaAyw{|;F}CJ^|$p+ zaLb-~N(E-heQmC(JX@VoDO+(d)>*^RQY+p?q?=LF~JgbPCRoX)O(Rk{#xM0@bH&XC4=pLr9cmqnAYnxvA`(7!P zX_x4d6jS*xW$PI#!(23t3rmLjXHQ0(2$am35`mk1=p>0e60%5?p;2v2Ghkf>jG&L& zU@Ii{C$;0h9P+1wP}ZKM-Qqt~I^CNW87d}}(vah;hE6+*5Mi; zUHOwE;CPTl2f@dIWJJoCE4A9)*zObk;B+#&*ZDyyKnw9~4k>QCSm!8KOF*qXix$Xy zTs|Jbh?@V0dvMPSg>jPJ`d+a(9{ z!IqqU(1mOo739U$?+vsPP74vH&1#R|6PArNKWAevK^{)|-*Y7R9(a%kBY*JkxO@N} z&YYzw!hKkmj-iZlr(2sg^@456zG8@lH6ReA@N{!pcgDk zco2_DLjDf%;%7#0-IB2Zjq@}ddVW&^Z6Lpb_Ud?knyu;Gi*qLUuQ=&G9e()Hb`In! z#OdC@ll~>iR{osE3|?g(Loa32t>0*K*nSkW#_C1M`hF5D+F0Fav+Faow{HcE*rHovmUqD5*evjj@AV`C3E z5=u9c(jW}or8FWX-3mi@N)H_(-QC^&o)PeV?*0D$?^*XNtyhG_>7#Yds(tVG(HrN0uY!$ady5HJPc!O1M$Srr)v7~8^)XDie8jKcCyqQz zjmbMVa2W!Pqg}KnhJk;OO1yjKB8aV1>+p|_^A8qO04$tt^9}>0xKM+H0tF8L=;RnY z_F=+bN?ofX%G(@)$q+ZJE%ee+?+OM($I&yLlR!0du<$s=IPAA$ZBDV0J%a^1>9gQR z<l+;v|yZ-bvD1-wglN3MA{|4#*!vBg6Fa=9%{DA-+^Z>JmaF^aU zuko)Pco|&4BzRRV?mMBMo|UZ`=sI#ph{Zjf_H@mxPxq8i0_K|xE1l)%cRHXQ z+5cbAWs(5UJ0`Ntv2H=1p`Uf5^>+c88TaIv_Pb@u53`#674=#dw#=yK+m^rB@4@+!JUZpx#m$gku9$}e0ePAV zNcjk+rPr-(6c?@sMMnV-bIn}%+F2&wJ+!^j{~tQMAZ=AVFQ&^Pg*`u6mG*M0Lloe3!2jo&5xOM)E z7+rE`Dc+)`VfrnmxV{OA3H$u~dB6nH2X$EXGOAN06{@~;E=Ay8+BtsE2C6aCg69$V zKV|C@uMq7L_CsgqZvC2neg-iWxKqq+9A#)d5O5DAcm${cv^T$t#g&6tDP-b^&kXj# zDhqxqyfm+dwoky>V6?tP6AWizDxi4wDW++Guuvx#m`V8-iP7LNLBlvKnf{aVbP!jg>`v8;y4}I3+ZM0b_(G zXR2I@=gq{QvTY10r86G3|9RIvDgYyR*6?1+>L9?T@D3h4iL6p%HR9?XI+aCX%be-A%{QJjeRHaZ;4WK@= zK>ucUHG1f`wX31=kTbHY;>4Sc+|uZn(f5C^?H?h_1cV*-SP8dpm6`4nc${~;o)Qvc zxMAzO!Y_VITbneWCbi0P)igKbyYYi`2%nZ&O7H{WVJioIMC(mO2cQHJ`~M0N(p_4B z4YkES@7(HO-A=gb^{}G)5+hHG<0ZfH;U!~cTKtOUF}%haKy=uX>wYcP3PESs$_8KkBq{7I4A>3vj2 zE&0&v8+*kh(>%3zR_KOyo8Wi^)njr~0om3Zl?h4LzfEWN)PU#;%>+p!3)~^!YX#n! zmhz~hjUv7ecPnH*p*8(Qka$|3#{``l2^{AW_9(C^P!6kgv{0#|R+2AegeHEK;yO&n zk2!N25c!ubAIHsxsWRR5k94es&iJIDZ}6(8DGd#aw)Ad&ALFeb?sVslqq^ z=XIDIz=^g?mXJ<_0dOC-`^9`ePDx-Z}Yy3yj{r%Jn zHBp^94r*^-7+m7%O|k_Vq@RB(tP^T89R&soJ^uYuk)*&+Jw7I%xCNkr5M?ta=WV#p zHzi~GzQMbM{Xgv3pa(n9yuB!JYp4Gs!2kD?t|najCP55;tMje0|9iBA5YNt+;`BL` z?~5FNr$v0voCgZ*2k}0`u@m(f@rRz$7n%It?~o}Bof7U7UoT>R5=y^Xg-b?p@F~Id z9`N8efD>3I^uUb&K1stGvZFOc^lNqU=)$0qAmQ+ZyeeS20BiJ{4B3;8u)S5FUiV6F z<~l-&hoPpfM6buPYdA-Pg9zQCe<$bs`4M$kV^`Q6yg(ZmP!8aVP~`j{WMw~3p-1Z* z+`UOJ`rew(A}zjv7t~6hD231%dZsy0+_@Utheux^B%wHltw)TM=fE8~!qWyG-%HiaWHV3G8Qxee26c^!lsgZVG6 ze+|uoxCr22fXt*t?#cLZ4S<+xx$!-vA!0@Pw!B!TMhMX zN3M;`{$>1m{lMClLAY5_BXS=Q%qPJ^VqohWSN$z^LuV@B{!Ut^F~pU%HUtt@)6ADn zd6H4&%o2wLq$Hyne?3J5v#zQuQt|fW6!VG?Z#Z~QlL+fz^c^E6C1jC6rVuXq?x;7R z9w6sI&Sk>?WgvhKdR2i*B!RZy_Z+(|yLHMndCW$Pmp7e1XnTbOFDbQkxM&)b$xK+s zYrdVZl)Tv6x8I@(U^ zP(EXPR)YM<9`|rmp@CvdR!xGCqzp9msM;#UR>cJv)UojKuXx0SHreYwKtGk4mQg zPuzkl)DwX-4XDWZTC{gOPuWG|vv58MPq&_S^#xG5a-lw7-awp^K3u3V9pQd{zV(LP zx-X~dtlBvdJOTnwTTCQwu}4)r8GlWgkFFjo7mop*vGNDvIl#2Us&{L8@*e?QTP)Nv zk8I~i1-XcsDJDEha&!T8Aa`!-*4BHTyxd#LI!`#=S2YCY9)T>BV8S3rIa^0O!i5V* z$zI8Sy**iFKuL5s@0g2a7!c{;K+oTDO3{cTwGkLUEq z+n$6^>*S%yc?L<4Qo8bEtnd5IBtaHc{OOuSR=a`C&{Zw^B(n+34^}dNN0|rGur~u~ zAcIilt|gtIWp4j}1!O{jkU0J%{*dD&8_%`lnZNT#gCS2opq)KdIamntVTTxl%4xa# zJ)@OJRA!FOyO0 zm!)-|&K>(Z7hD}M`$GSaO9Kwneo0I0$@(|U3sjhgv!BGxIdp)&u``MC6d}k3N(_De zqa`FQ6XS$}hTzUmS7%G{!-R)E=1-Z-jt1pC^wEwEM#Jnex435bTS{(K0W&{9{aMC3 zC7J&s7$C|U*A|EM44a#$=35RHp|K9~N@ zv4+Qc!zd}|k05RQ!E`vDSCuMt2}8UerH9}7>?cSCJNZLAu6Oa3H_GW*iM9~zwmW&HV;FPax5C39Iu z(Ur08n5In*8gJWT8z%V6MNl?~Wf2yTO5mso#JzK>?%qwM*3e9hb#eSl>0+VF zYAovg7$d&14;4W8Pkvr)zv(K7>g1Sw8drDqaKe0eGduaJSJ`Z0oS-@?Y=*=zd$?t`#6W@Qd4VS6@{-<9VosSxr@9D zPU!L@)ab?qyC1le5091stJ3<**J~mRo&I#gKyUy5LsiH`3W#(kC*u9{SW(ZfG;<`kjM^HbZ|sWbcx@| z{f03hZpP*EG3lNfZV?b`>U9OHq+n>*LZ2*^8zaIIyOkVDoi%Rwi&KCwNJEs6#80tJ zy}8~ZS3+757=d$>oka4t4Q7@LYUrgy`u;5~5Q)@C(@-X>Gza#PZ7)xM#e*_mT4~n@ zvYf#tRV}FTY4@%_JLLIpHvuTe(wEd83uCYDBr=y0ATqN%yL|p*Il3w}T-cp%UW1KJ z@Y$R+>zK9wB+cYU0h) z5dZS*+msXZ%4?xwU_@cT_&+ZVWlV_`*K7>6BR9lHW&>3*o4s)Bjg|HKQtVO-9ZkWQ!k7Y+KR$a-?LWX}X*G1I_L$(^xYipG z8ah13xlHjKI;^LCZz{iC;8n}Yhquobohf;BSWhr&dkdSakY{%&w$LKke_o}{2q<`l zylGUD*-8#JV-@3NhIF;7L5}0&C>t)BD!$MSe{UBr-GME&rFYRODlG~@Wm zBexARLcr%LGG!zL1> zJe@>Ne3sZj65l;qE-EkO`L)WQDqUndx1NiP{1Xl=09^Hjp@dWDXu{ zzCdH^@*rE)mni!IcV#u=OvUrAdYn1!g907iK2B8?=5%0+i)nfqYH0b^OH*My2_Us= zByrK-jXng7hyS@a?`B;AOV)|Lw!z{*@M#E#??dZ*4#n{2xYDCWhWv=Oi~J(uZj7C@ zXv;7egz(`;xTu6D5emnFYHjP&E%h&rk3e0fwj>?CeQ_{lUkg~_Y0^0gP(}_q1$M(A_NO{= z(Xf~g4b{ynz70P;-mLdK~T;?h6BGKAR2T=`;GDZ zhfpI}N|zGHpmX)wh8XGSg<8LvkzD3alC`?o!W^B2Y6~#uoyC&?O-&7!EJgeyuCzsj zs-EI0ti?N$TkdGr|3GmNb1?>W)GKKhS2J<|6OPu69Ry0*wMJDPcdq?jx-ljc zmSv;nsi}5S#fG%XmaWb3_3?Ry&(HMh3Oh}I1k+`lRZF};kb=h!T~BqMW!|W@U^N?K zeB*uO4+tj)_YL$CHt*W~!^zonm@fyE9#G^5sT;D7AEDU9D1kCFg6s?#oO+N?iaAME zxd;&OrGvh`a4%v`e`Mzp-G2fHZ(yhQMeQfT+evbub!$4-RhA#$_4CGp&&3*l>1dmo9=-yt_kfI%$AFww9MSLK(wFSn{Q^uus0y?_7OmqsHH%!xxQh4>f>k zA(z9|QHtj~g03eXh*A&ml%CM?P3d9vRA-A+l;tJ^HoR{o16Q76?A zZvS`C#Id{@(ajmKqiXDZl(LwY;?*04eqBvS|3zqN&hYlj&70FJCJSXs*qf^p_s0f~ z-L&zHn}j4Jil>KZMc+xNb5SDIqFSWJSRcbG^Z;>^8Cj|yGiHV#oE|BU2He!GhCLEqZZ3b~y7QgTNdtdKV%?2p+e4bK zrul~1naFWO`s&Azb5QP&iBGUSFRt9CnFtAn-4=IHuAl6mAbMyynVo_z?57!;s{Q^( z+baG>*;bG$ZSS0t1P$+|h#ms!7wdx$KQ)2#`NJIY%}e{?KFNPM#w zIivb!Y4^$2xVtz+6#k9G@H?h2iFz~F1!31UQBh6@&*~4d*Owv&W)=;?)$o_y?02hk ze&CGk3^~^21>^pWF7DO>V6TXs-qxc)P4QVr*rbkBiQQsg9Gqqx(TDghJRk8^lqHNzJcezNI1w$IJ;A;({Zrt&I z2T*TPesLY-@o|^0H&wwOpz8$Y#(j-Cd4SAH9$LAjcYS;%rI|i!wV2P!ds%R3sX|C{ zuAdkPVaTGdVh-~e8A@`_4sPC{?T!Zjsr?74zv%&y|K)^n;&0qC!N-#`{OB!*cF9d$ z5U0fpoziEy5)x8s1Zq*>eW7+qIG+0I4bi>(&zoMf5C9UtxdhZN_|aA0sqp!dUr-q zzVw%+x(dv%2v}kg)Y!$nm(BX$Nk|y3@+MJ=HEM-jUiBc5LN&T_VxN-mzNe|oubpD^f5Aa6-@*?Y53MZuI0Dn`fXH%9gQvZcudQj45 zYWxB8t0L^GL208U#ZFVnkUkIBBduI}_2UT_4@6cz*+nErOgPL_buZPImTPvh9uJW@ zavx-$3?hB3N<}y#uE`Z3PPP;%&6^6j`sIL~08EeSJ*XFt8ZT$ZM!J|9=ht}G)^ZUP zVd(HjV@A=Q59iVL_0?wPT;ad;!yEbu5^Vcte zL^5+3@m98s&b*yMj=ZHZ5@Z=6LAmQ2;R?V6zE^##u-bgz`ahnqPibE9#$H{J6P%D* zd&6p1GyV`Cku+4!#5sMvK5s_=X-xL6X605MS{=Vov8zdB&R_qL9~rZ^-T}1I(Z}~x zi{XBCm*^RvS^-}hJu`ghmUzj&L?|K&U+%UBYOc(Z7^AKKGg<(!i)GaT0Vo-??|6^v z1{_4k6pH+YSBm(_(~4A(_>cITY2K(YfhEY+8F{DA+iiwp(rUjBT?FA7i}{Y)R}4Ih}?MOGJ5E+Rrv_>=N4{~ z59>F58sZPl1Qv!~oV)DK4eQrb)DV8P{&xJtfdnoT7~Hywu?e(JgK2m7 zVpOKf#kL^K=oltC{TuYDo)?h2dUDSOU}r(RKvh(dN)pB~aXVc2)048m9NHN07X#^X z!?LaIPjR1jWjt16Kc79LUT$_>-SwTYaCx4~5Tz8Y_%)}UJWhtSL({9Jp6O$6kPMaD z^W1g=#uL~T!zroftk{(&4outdNe2Qez&Z;)xdz_k*U36`Q_y`Q_s{Ng7@3?h19ClX zxE5{ir4H6=9`e|M9#?cGv_MwrAM{{V(tdg0#*qiUp$~t$Gv(zDi@{}iJ*cUBJV#(2 z9v-!zmDl`ab$#?zg^G_8?qk8JR0tcz7WjCaA!cfT7c+q0QTLIxHD!}R#cuhZeQ(gk z8c<+_-a`&v+G)T+x1`-Zv+%Htc3h8vwd_7mTMgr-Gd7WVc#8`Izd>H5HU}w__pZam zkvE_AGg7>JJ|#2OHXVJ0a6wGMWS&_MY)W)gfkh3|7J`frRo9NfFP9C7!FY&vQ7xXD zZ&_%h4<3%wCas;zpN{2H%pe}iA6ZU}uZ|yNgojv$!sd(Uk4E_Y9<#}U1Se_Ufm8ch z=zReGN#HQBdi5SezE=%BX<~XWyc))L8pju3aagVN_ssE4@fU{{sG~)zv#A;flEff) zMT^3K0zk@CMGH!`cFBmfbY8C!-y%FW5xN3%L6NfZ5Sk2oqKqRSRn*no%8wtW2=Vdg=?!`}2WkiNicQOEKVJ z!j%tp-|Zk2IBmMd6h+Mx67kB-J8+}?t!ZpAT!H-x34*m8;*L{-7K#=%N)>Ojy_!zj z&2m>i9}zz74NLdp&r``mQ6MAyJkH(snb?R0k!6p$HI$|$l%{v(;b-C`F7MLEcQ~~^ zhIyQ)numdc zPaiU}bmVss{YoMrnTK23FSu2;7xvm@?~Bsx;LTZN0r*uA9&``)_`fic9gB#`_%sVm)aB_wGcflGJD%hMVukyx7;XXZsGF#XbQqpYs_n>y1yuX??s zA}w0@>b0?UOOM*mG5s4Nd>CXO!Yes5)x<3nDumay1SK3MA||}_Vm319GhW7GOep?Y zyBD$a9_eH}Lw@$8(L%Sa9br}R2T;9WZ9GMb_yR~QU^zO#3j2ye6zoD@$sew#SPWT~ z7|DfC46c{gR)0%UEqJIrqzzeb{g|Yht&-F`0g*JMx!*0_&CPeva?hT=q|2PBzaX-A>Cj< zat?%`Y#i8HCWD}8VF?6=SFao`WW9R#P>E3VC2{n}xTC$po42-RUD=4xldnQ8fqSau zhe?tqZ)Elc>**{temlV2p?Fl7_hi;h%%k7uO{Dh0Olb`Fm@l}J=}bHLl7_&=a^$_A zUJtL0Ij}QH8Jm)>B@TbLf`P&G2{60|5Shn_KB%y$r|Y z!{;C8l%PO@48O%{6w?R!f+y~k<^wBMHB|^>^gfjNp++K!aeYlGtUHw_j{z!K6U(Py z7T@Z(t296-et73NQJ^@Ios*1@#n1)_t0E)~@`_g9?Q~%0YegRJCMgA?nsAOsJ$n2$ zzL(YWh7nvgk0zQ=E^pRnyQ+O8u^B)%dv3m6@E2`8ac9qXo+@}QT;!3o%;8N3D75T< z8*S7Nfe_;t5IeAWU?SZgA%P6$GqEwEjvT`u5oi6{6TY3h8#S>c(QGZ_AuagW>Du*7#`8-3TEgbPs`^ zR}dIl^{q?Yv%sO181uT?5A1Lg=q~&?$KnV-(+U9gw-P4x-EhFuB7Tt|QM>F0VGOlU z)sG&x-_V8i+i#dQhmct3%-4dOVI%X?3-Uc^X0JnLsOi6)5 z@Sxwa1E0w3Z2G#e_5+_UAHsxPa*clKPw2q~kuyg&A-H8|*}N?&;xD-=C7%W`D9;QR zPiSXpkdCAgr%@VZ-njocBXiFUPB7bk2#lI!>=;Uz#;hT;jHx4>$gSxCxkrQ|&j0BTEj8F+>t;g?aSVPKPKw$@KP$bl_}EkaMCqbufftdb zU<`2>=Ua%|Rzpg1YAOQk5e;fzL=_fifnrnq)+ww3}bES3SDg!eEtrqi{_(l-W! zgX18VpG6j*iAx-5Oya}59`H6jE^G~IeAH*A@5_m`DXSY};_-buuVPBDv~b*l-GVH_ zX+n^HZ^oESf)B?&=n1bZQAN_d0(KPvl(H2lN?8%gyGCI`CO3m-;o7t5;UyibEf*Zg zEdfAD;Y(f%11D6?B!s%kDfw*3CNa(UJU(J2cfRA6D7l_Ki7ei+H6c!p?dn&l=6xT( z>($^uqCTJbHA<`y^_*lOV-&ubM$>tvg2?uR1i(V6Pi!#IzRJjLHmf7nH?R-h6@aPY zvpjF9unKWs2!CMFplS4ty5@bY>m)JjZ-#qn@EW89#I>E%#@H=AgbI0nsthRJPG4v)aKouM$C8rYvjy|A_B4y z3+xQ6Aq2?UE0Pp5TkMgh!zu=eh`bR{CCB0$u2*1oWeM^7=0GSENSHt&*ZYbOiPl*L zONfg3+E3mDHElgMkcuIcFYpzu594?6l0tj3gh~LcCPvs-pp0AODcQsC#yysD+SdUl zcG7U6LZrGl82m3X-d{f~;d!LmpOLZ`4M&wo>?*hP#xZii62zK4ZF{md0^RcpJi1hX zw2SSS$`AQt$9iDD7A0?6vSOUF6$^e_qo-+U#a})F2--1IRqPqFPYqy9M4`p@mB45* z5{Ql#P{z2%O>N8kDSiy+F%Nne9G<|>@rfqx^bbFs?I55hxVbM)heZ$z%q`Km*P;07ry9;o=dFk9-RRyN@}DJ1ccm0Bq|U{^bv8qch)6D z1omp6-|46#4(J|Lt(quRM`%Kz2Ul~+nE)zQhSN*Zn*Dl*M=r}nL(XV}ikP<{o9qrQ zK!enik`^`pg%#*Ff|YoxeHbD<(xdAhAhF+*%-GmT8!&I>;65l%LIBom+>(yA5S@h>t3o`zl%^Y;|sp)m$ z?8BGTeZR5!FyZ z7Jhcb~{;TY1534 zmQZ?*5EW$SFLrYhYp=sLRQpaWAm)i5YL$kcDMn?xOP>nDJc)zJ)V88c@SlVE|3EnQ~iaz zSF5PPCq2EnIBNQv%yM{hF#81jQ25}5_KcQY`BAl&VvBqz+ILb=^u_Bw{zNz$J39%t z5%#4rhQ%`$+>lfh#<}9)+H!Yoo#fb>;pg=JW)UNg`j+{L95n5WPR6GP&hPJH3~P6~ zCb~pF)%ee1&>znmFg&O9`gpF;)vp5bBfZEQz^ImHsCcNQ zMnlfA+uQ`jn#~@u@i}rmCAN-^*1XgOvN1hgW2HsbzgY{xv>7EJL(4J0u;jjOz35_2 z2C-gP8lAFD?ey5H5sOhg?ZFf2c&-)uyfLuI0s%>WwDv>*JycA8ez2^_g_4U!rR2Cn z$x&1kn5Md;M3<#Mjz+!DTw0XoM*cCamctP|z|*q?EcRV06}a=QNMVh|0p5*E9l;KQ z^6m>PA>6bn%Q@^h%rRUvy0SsWKJxiSjC|@hzV%a#EriF)9xewpj9rWFYhMlyZ*USx z7`owh?7p=h0?J%+2ZjGS7YWWAyDNttF7#x;Ptj9r2-3d#x-mZx@KyEo_9UClJ<0Lm zyB}+p0Q)ROsN~S%_`>#hw$?uLzqXClqqRG+4u$NZWh!IM^e}|zX+65lc z+T>Ze1Z+u4U#0$wkb^ZBga^#8Taev17rAyN<4;HX2Fp><2U{dXeBkQ**%Fy zA8w-VWz{xY3wN8x`MLTBepB=3h1QH4Dg8${Q$(50tt2se(g7nyQ1Q5iHOm`F28R(y zGW`^rS9;k|Sn7Cs-N`kisgk61Ux6`?qLJ~dkFiO7yb2WE|k$_xV`2~ z%ySP50#5fjei_Q0GbJB0&Z3@)bw~G!E*y`<2sUgsHF6u@%rLL>a5i9B{MCtb9*sU{^>XNj^EJR}?T}oHVh#doYeG3-@K;u=&YS>xEAH4-ZaO(L0#*G*D3 zWD(*HdWTyivpDYY*bXK-?S{(KdkL^c{C;<0>NFGxBFt>D<`qJ}APRY4T%V^btt=7) z$-69GTIfjU@5i~qW$>Z_S!;@BBQV&w$GMushvW=7Gw9FmSx-tBie^T<7~e{o#6c!L z(`*#btTGjBdE96|PQT4d!{ua~*=LeTW5)-M+B_Z%0J$uXZRYq7XEXnJyiB--#lHo{ zp1ladL++K`+#TIu{#3H6Ni?^(TM|h66Qy&{z+tWWnjwIJYff-Ef#LZeW?-!f$1Ha; z;Oivb{4{h}hupCPvj|-6Nxq@~tl2xL{F;4<)ZgcbQIdk-Lt-SNGHJf|QlKR(0zRej z9P+&7kkx2t1zS?Od7{Q29C@+yCO18N+;d3jA@_qX{C1_abTH!K0rFgFT0@UT%MIZ| znHDNFnrs>QcV4yoaFLuy+wPp()!$rj^s0#@{7jm?Ji@;_A$BoBDCX6(jWG%8z_whZ-79&VDdtr9_W4!U%>KDn zFip~h#%P$P9kJt4{qMT_lLY~CxO-lEgahF7?&mPhAKq=6q?`O4`>qNX4R?{Ngb0d&i6_{w38`I8 zL+Q?hAX{Q!Tt=x;frVUuk8nhK(8WtYht-OB#t{=6EZZd}hC2*zoUzX>hQ6mYCI)X% zwZcQJ=j6TS@3Fl}^xtpp9FSNWfc1?oAM`InFM|Z8 zx8c%ug$22QjM`4~n@%CU1u`Gc#~tvoJkBv%tc%q%yuKPG8Va1a#`a_ju)3SR30kh? zCRc9aLi0+Z9Xh-POcjwBqOo9=U03uwHDhXgf5bGK*PgEs4nC5Q>VKM_w<2--;If+k zUf4eP0=nt9R;=MOF(%m*+=E*8IxZ75)fR4t-~$Vk=&u^unv!AQL6}@vdb|>nUWfDs z;l22v7W6P8KJ|NNdiMcfkdTZbsl!8$!({=hkAo)R_^VENiX^(ig0OW&mnUFX7Uxu9 zal|q10m?5MM|URD{>$-6@2%90^x&Nfz*}vaHhufaP&A7Zg`dePVw?K|Yyz=#F73zw zO!BGXbIX;1R+cb*8j`@+t|fWqfC)6$A-m%eZcx0vovocYN+OX#!^kV_!5ea7D9yNTcMfUC)=O_b}Y3e7!JPFu8Psss`=pKV^}+$BwIp? z&S@UL_0g@S4W%c9Z;?I`Z5kb=^^mvj;Q3lJQfP{xPY;q7NA-KYs#1~%(6cgl0GH#i{`L_9+rmp$|A?2a9$0&@P!B+4AOHx z+pvM>EI6hmG@6ckB0EK}PC*DL-j7g5=$GM|V0>#awp>BeU*-yrsuQB7OSEvT=VS#?;ugt^&C*y4I+-3ERn%Ycax@5nFTKWehA zRyR?UxSrHi_d#TJbzw3caNuU;U`0j;0P_H`HsZ6$SBR_<)qpHF8shM&37zgUMi7 zFSX?8Sso`F2D*%2<#0vKS`~cG1p8Ntc_W~pX$>bk>JMd5p98ALQK3O(kyB?=C|SKHbmjKB_?JIWp_u+f|joQf@OSQCnF_^sx1nP-O7mX}N;KQ}gWmLnypeZ_QF{s>^m z&YtskVa&_1tBKz0ys><_S4PJ5laB3@6`~DcaN~tGnj+D-rUM-6hxxA7+$x2x#G?f6YriBTR%7VsCDefUy89ImL}@bPj2x!B>PrCG16fVn*IOG z-5Di3s0+hl*Au}ZMz{)Xtyah_Q^ymL1wzf%?js35WE&}R$hT1>g0T4;e03sYmjt~7 zhFtkVb{-A~s$i7{(_{FWd?|_->og7bXF{r^LbE$8%ne|RjclKfmCMjNdB=knu)=N2 z-M>VpXI<{gW&e~brVX9z`CQdf7f-yNN|X>09* z#HpNpsJpvR-EnA~>6jn9g$*_FDKH7%9SYgFV>B-Pth{*|luEi=5VqZvL~SWpcMelq z840q;U-hmI+ueP4MV-)*EU~-BM<0CMbs<=YZd5%jae6`WD)z={3uD;VLhdQsSln9b zoE$8S|8EKGa>U%QXIkzv$nDFH7;Jh~JMS=gXsjhyV>dONb5&SeoUy0ZZw_}+a+}71 zu*nu2u(f#p*`_{$K7x3j)Gzxs$e7 z0*Uw&sA-Oi=)iDAKG78RpWH)0X20g_b|-j_F`!P&>9+^G_%2rh=1-|dI0ECe>A$gw zblB@Pd56D0LeC%4vtyG_AtaMCQGU$V?iKeA%iJY2^5vHgK~?$azxI}ASz65rKwY+b z#ow3Tc&pRh8%plLiL*SOnW6kK#}kp{Crw`FA|8MzJS;9Tf|{=9n&#ng9N!&}sMPC! zuybFW-2uCL$0$U0DA_gcK0FVp5;NY}8ylvW@HaU;i*#8%71H)9c^|%W*mYQI6GnFt z{IDD``DE!#OheRWv#)Q)`jyTTh#Th3b32*isEywZMeYT~`Z{|44)80%6B`%_eoDC* z5f25NXl0!iDu>lS0YUj$7eD7*(=r3(Dn1K4>k#^fWMr!uehcp94j#3+T-iE8@=l24 zZ;-TN_zjS^hGMx{pCX%M?#LlmrDa+ZUqqseY$97y>l!Z&3G=o*0%JIt4_PO$Te~&( z?ys+#-V?kKT<<3!f5!$}kPQ{tFoIat&o1}8Y)vR~)0z7S!;x?{5NOQ5?&<-%C7)84 zl??hk`&!2pc|h;(Pu?a^y6hs9iVAyoiQwueEUB&ndVMWwu=7)3Igo7fP2RlUuLDO}hAzRlnMEwrco=Of4@>vC>s1%ym?Dc1*o!0LNImOrOCz zj2r;WU?lj)q#(L-X3B#wQ$YBf21MJN{cBVLsY`S|y5)isq z?Q42|Y&}5~@(pbL)my%9W#v9!ZHG^GOtibZUgTu0siYS$+Ik^>Z+inLcEklNm4dXi zyVP!d!~24TuC<&472I+2iFGTO>{=NU>m=Ah=;MlSD+H0NXxz^TDz)v5g-rA$FBnnq?RCu$U zT`O_M74A+!>|-)c@=&W}ITqdmRP9!p3o2?akp+lbjx8G643>Kp+a+)w2pN4J-KZ)+ zuEz&P7%)K(Q!e%@w#=SqKyTYbS**czF>{2fEDHB+I#93iTEr@i@R1&>AeERcdzYgW znLo6(A)e}cQ_9U$?CAr6Xl{`^>&qG#8W(2%iOPUn55RJqn8uGk-uXOkN)#;=fI#u* zt){jup745T5;L^X0tt#4(61Eg3pAASKYT=Xq=a)QPsQ^)>c|8c|iVN|-m6*F|DmOI)cyuxuuw;WcE;Jvl zh}TEY?=HRk>+013D{qL1*txskfIa6$a?~-wH;Niu@XFt?XcMRt>kE(3M$FC>b7f&r z8_bl+PNx1S!@@K;-m|HJ;ZU`|d!viq5rRq>EV!PO9%A>LvLoSHI2B?aD<#dXzl)-R z%#~p}(7Eo~CT3n1*VPGS@O#S=f2dE1zo`ITz|MnU=q89^Y~Ks?-P~nU5{Yl~X59N_ep*r`B%OY5i42FU;#~&9 z)#C$&o5p~7|M>-I0`clTD{Gy~eK==@vNusKu?|DZvCI^^3hF)R%3HIg(xwu?>OC>= z?U+C?H%`yme}t&Rz-W{g7YaXzcO7aKC(Jw#kT~9s4jT<}YkU0tk`$b19~@i7u)gZ8iByWp@4;y0yYEqrIqbGOsNg3Xm8 z@z`_{J4Tal8M)&E`SovyFXvn+1=bwXO%=kZ0R$g^XVGVnANol?-@;DoW3-dft3S~5 z9q^G4@PD2}L^I64VG&0LBy@eMG*oTtmoui1RY4r|aC)2oQ=ShU=r-ko;zlk^j52+E zxELjatsx;NA!cal-1BX_JX5ZP4d!-)5jsl?W{D5$Ug6$QiU->Jum$)GI7g5!fa{~B z`^#9-*X6+M15f>*QM;s}8ttY+2YLLz_kzl6e!J&+ ze*_VFD~AaMOB2fSy$^mbA$c_JkgHIEX6NIa1}0eocU1n+sM0Q`7sNXu`k`m}bM2M46~=H0(9u!%gqrg7bS;-pRKNj>3MWzrQHbe0XI1b>-4hJk#855#TP_Kh!Nk~mwX5qWV>+oWPvF!I)= z5{*8!t8P&hytiLT0$)v6{3d%W`igc6mXL43@9-nI#2+?z8jnFd6M2JA&`BIjKHE$e z>YJ7^E|h9UsJNZmY5AaH$g*{&H|na;?=P?)$pe*u;GqMvbWrcz+uiLGdj z>_x>>`Xu_y{z-ME#|z63FfV*5u7_Z5L>l-?MctIXQ{Fm;e-68#57LcywaZ1+QPlm0 z!zJg9ZUovG;h&!6kFfE}4n4-Q2~kXbo&2_a8_9y&xhmWFfdPtgFBh?a^L~v_gX8S4 zi3Sd-tE`x_^aHf3{CbTr@T2$XgOT$m%`PEqgj1LkIiI+Pk4KTpOb2;fIKme3Wbugo zA2Xqhe;h;Pyq<+u*&fD-5;-imWa5(l)p6rf=JCRLl5p!BcjrT)*PZ(io-EY@nE^9*y5}Bls1Mko!aI4 zS{2hXQ)LgJ`6|>`R3eB5_mAr_f>DIoTvZcvBA{aUJMXAZPoa> z+1{k$Wu8hRr#9YHyoy;t)aJH(wK-a<@~VQT+!jc!A&-&174GM_BGS)!gjV+Ds{Qi3 zEszpMIFmo$pvzJJmeTYvwB+c@>sQsEd58N~1O9Gr^k#7KZXsZOV55cia)D?_F}wDn zWzPWSroy^wH+H%C>02r5LqJ0EIT``E(K}bX4osW1D0&JSvBtwVIGqa=H;IGXM_zLL8SX+5dBb6GH@%q%Ljk4h0viVQA=&j+YZ-noOj#4 zAk_JlW`|j2@WIY2#TGXsvKI2ztS7My_3|*ET8QFZq>-!$cQgrx>ehHj@s047#f|at zNfU?8h;IP zDngJ;*J2kf)~axXgiC1!2g46ysk|^~diSg)C;GM&xgh6`sm7xqIQ5Rb7V0QFj+Q14 zjdMw=48T`PqCkTXwhOFe9?OpzqmD7+MjSV|pTFKqDGMy>(PJ=vY9>+1J>g&VPR!O4 zr`o2A(X=Il64PfIeo6LIeCzbI7+ze%bjZ=~`A(l0D$hK|SAw}jo*4a!hA_k!p6ssu z;*}C^H>`>^#zv2dt`8zC{Mp!*7V0#St4~^_#`h+D^^|M4WS|1)kCDtX0y%R-g+K-8 z-3RVAl<|iI~mGSGBXU0|iKSd(w^;4d3|R1#=pvR%)I zi)JO!if4VH1ppj8WF)wg>wgf@+A~JompAuf-AF)zJS6ipk*gj%nYvKVNyBiMyX{!e z11khdg%bS$0Q>BVw7(jZjzp_d%-0Ov#cu&iOt8&M+`{mSt`C9wRCXw)ER$i+2B-O8p1ldzLT+?F;8yMR)N`tD;B`)BJ$8cyhPA6X-u()1gr?6nHDtN9~hUO2Ke)RbN*qsYQ|QM7*IW$79VLMANo zt@ZU%%UFKC*h{X$pQ!$)Z(aSK7k7TK(5V}^G0s$z^S0js_Oja@|9vh zP&G{stSNr17!({0rs5(}fz9$RGVyV{u)dvAYd}Bnw(Q$H@u7T^!OvPXT$KBeNNL)S z;Gg14iW#o|MN;S}pLZ!&TM{>ZlhEP*#TX?tCDD?@`fuj_kOnL!!9Hu#Zfu{|wgzHf zlp;`|&oZ|xb&G?_7PdZmNO9Ke87t4d zspmc`u3cx6nUzZ)u{fcJB#jP@;*O9a4@0RkDn0JEqN^@}Pr(8t$2_FzWJsCW2ZH~?w4jJ3i;$D0tz*P5k^AL6#nzHjBvEG5VN%8jhf@0i z)uv(~3iEN@(kCS;MpSYHO(SFiQ#?>@=uzpYAcLdoy2=j{N}~9~uRS46>1d?ri1jtN3G~o#ou?MeyZprl^x20w0 z51n+%=i_rRHjKJq4&rSFsVsCuI-$IUc&kK_AMV_L8X;na<#_j?RrPdjUc^orYu)1) z>wBO}(fXy=7Ql;8^_@=cGA((jp(~%f9w$YXyNHH+ahI40{xE&T-pRYLRItUeVZ@p&mQH zBWoNjjUE>9gt?LV=nQad=qU1n)Csh8{Pg(sFTAXAv^IUT^qi`K(3^R&4E1NxWxbVU z=LOdtUPtpk!g?~zPvuHolqg_F0^}5zVUa(Vb@+I}71{sZtGh}7!7M`m6Zt;mN_&h* zY<3=VJb`c{fa_}Oj|WSw6hrYj+0*t!$PYqf=OG>MmK?~^RrYGUIy$SkT71&7n@T*s zMuW>YI8lmjVD9QSs2pUR#U4038h+dGX#9H|tSP$n^bP(dC6)rMBcTzXG4OJAQOe!D zRGV-q=84G_*_vVsj^?-Mw9l&13dkBU3nPq(yrX{T9Enl%_Q}yAx0t4-@+?65o@1++ z>pvo&+1<3KI%c@V1?h8MX>L|+JuD!$sR^Y79XPb!DNX;K+W-M@cZ%ZtDJ|L)Mf9bi zq6snq#Vrk2&ehw7+Opb^vCqlbw^5r`-9p%*>q&)od}cfJaiW}A#?|_jo30G)m2ssR z(B$vh`55ZfQZ?q_-APA*!B8TIG4UzHB~ihkSA}b~;*%LMk^)MO!uWY=b3;P;YdjJD z@$UWlbmppn>+^cJM|zhOtqlS&;OF-f8%04ARPkvlwa)LdsNt_R!k$qca(0T?D(Fus z&CJLgE95MCaq2FxWr=66i$}ooUwq^XfR8{D(ry4(dPnf|0TN<=mT4#&!OwBQxWBys zyo30+yaE^aEl~N5hF*qkkATuCOTr}=7!d2RaW9N)ltf`q18GuzhA@vQ?E0&O6`*C?$2yZU)@&nx{6;ajz%Yz_2~sVApK@We#WR#+ z*(B1E)ty`TMj8q@WBJ5IntdC4iqP?`?T2=yeO09&;kWCh{% z++t2;_!(aRNL(Ya6KYhWPqm;~Q2` zbNxPSM#U)@Y4}w%g6uc!Q8uX0Ns^|7YohMj&`8TfGlsyW=8ut2|=_e_(67?I)ua; z3l`HW-tXho=bc$gtoshaWzGf#fnu5xaHs7g&m*zqO`i6ch?2LMG}ZJm#TAm)CaWAA z@5Z;C=ro?FzCbc1)yU4Je-LdXMyKJAKoAxqGWIF9ON%q(XVcW2-TQPccH3jG{)-^E zCo->z2R^@{^+*Bg+#zBx?r(r~j}`IAq7G7|Qy-$-l(o5I)#=@3k>X0}<K2=T}JqebEtOl4l!seZ+md z152&X#y0k6La>yNjEwaGfx0&%=}{xL#_fI&2Z^J8-BrfO5YHjrI_Z~I;#cJjPIPjv z;vNVC&zzRx6{~H-6(;9XpxCsXsjuFOawx zB|nKWIO7a3D7iZuuq70hjNK8%QQNUC!?E3o#$QE1lw5YYmL%G=s?+Gf2rFtIsG`!w zsERZMs(CpYnpIiO>z|MOF9D4OtdJ7@-k%K1Aw-~GXVe}AB5y_+tpu4|*yo1s1e7KW zpa3=H@$VI?b93_YT4969^-YR zSDy17CH}+q0|Gw)RMuENZn&Qlv$DTCh@LRu^q2QZ8I3cj zp-EWNQEG;{>5;fMQfSf|8740NVspbS@+S7BdvimGwD<7M9eF65WlM2Rwfj6PT*^_W z9ebAjq|LAHCSZzMPD?|m9uVi}_qllv4u1aC`{Og#;q`ftM9rcqWqJ08O6h!_djn)c z#H2DTDi#hfo!)Ngar|LkgF+z_y+J2A^{vutqG$b|D{L|wm3&6MHil7^F@w?3`}-;! zHUT6^PE}XOTYskX6d@gh6XYgZA(bR&+!qD69h&`u7qec)_Dy79eQ?~(+fn_ck;F7D zL4Gkpdw30_JbrKXk_n0=PzHJa5Xac5<0JAd-bbJ`K$XRI4yQ4V5vH$5BEIPZwWt-8c=MV`6Col zHcAcX`=?&x_B&8+E_%UQ1&h0X?H#r#zX?)bzJZuLz$~_$hTAoSS(*CzZF+|Zc-me^ z{BeW893gi!fTXnH{ptFg0gE-B0=ewC>zte#UiA=7ad8i|2pX>Krs>6+8HG;cu&rcH0MxOiP>7jip@hGv$wySUJosP zRd1l>oT!ae!xbE>gF#8KbHrh-Hn-l_t&5T_`u5MdexTZ^#z8`p zi=uIsNKSYsiSW9UvyV%QDxFT#S?;`S*JUv&=tHr7IJp~P$Uk6yjeHReg*dC~=UxGu zE`Fa=?@S8F2dpgfY3qZecROlxwvA1Yio3TsmyIm5LT^E;>+_Y-mu`5M9?1L}D*H`{ z0USTJuB$~VJMXQLZvKwB7Hy8K2>fH7a{lR5x+`{Un!P7}#&7AE7dwA2hEtPW0^0f& zb?#){l?0MggnBNz`2)Zd?Xe?IZl-hiXy06UnU4I^b;L3pQ(qM(krK;hfmIdTtBbhU zd8%2YHHjLxrl%2sWW{W&4ay-sEz#(F?wtO}Fvs-s3QEj3g7~JdHmei9Ug@Pq<5wY)6V4#u2Kz!!H}#JMlU) zL?#g=dn!V}q)h;CdLZs2Nu0(sO4m0LPEoEhYWy@n1CQdUel&31iN@WG)|rskr3!?< zq2oKTxV{$-Wa7)+AE$?@pFJ|~Ne>nKx~u&O0O5ax9uK_@b|)hWaV-H(NgoPg(pB)G z3C?IZj-DcCZd;JH9EP6mqoJpt30yKCH>mjUmZmY%1F>=VxPS63N_vFMxY0iir#Cml z(0%JHD?(7vx7`7eF_ncovzDK4nv75HY?qgF}*bymqYrxUZ(t_{+hwl?tdqW`c2%yK|T|1@r7>3(KsVF;`T zMIWPDof}6g5Jbehd?HPWH!v*{$PYwTm!96*4m>P=7}Oy|tm?n`1cu8_sTjD?4i&+3 zN8NX^ZMuAf$@Kc|vXjk`iZ*dj4>1oS$i-Zd5ajD6{H?@G zC7%0+Rmb4&G;oY?^k<|8S$mM zRrz3P+)RkWSv-(TkEfPCX>Z(lK-{KdtS=X5iXg)MdwAdg3`BQNY)3Ip`sO7P8^dvFVarT*!%8y->%)>K zFT=vBp@iR-1f||T-y^OUjfXi`iH%+*REq{fg~ESrwg2411!x5)k#$w}meyZM zF-;Y$_H4OrB9{s_bbr#Ca!Fuw*|*H?a_=K!5rTDEmP)8-NPTKd!O)ipBwRarI-3qxV!AF+JRY4p?%3j9Q~F}HXoT+@X%yjPnzFZ>dRV&&H#n4(4gHml z(M#gP_kZIzur$m7zz2_CZPwne;Ty#)EJZX<9IaPxLOy(CJ7;9?<*ayvp-t;&wUF#$}{iiN5Qc{L#3o>^IXA4yz_GLPKF)(~yJ1im7~zdnW?T zACUF7FII)yI0`=QhF+W~{BTG^3FtR6vMDhypi5mER`4i5++>~9sJ(W7pz;My_&-8* z47gAC$+_$P_INN*M375DlYSx4nW|B&X^Vo-fTbAS3zmEomz=IJrT`8M@8dI2bo}NA zOM6FLCnz|4+`scVb)!y4ni<`qPJ`BK(9d9Y<%JK(YU$3ZqY$E)6ii(^__j~ti`mJd zrx045_!CQZjgX`t6S|Mh5qNuv?eQWJ9&Cblnx%`hYxS$Sj4vnTEftai4#pLT=SNik zdq|RiLo&QlXn9YD?+FD2#q98n<2hlV1eGvNmY7)g`+^js&6mB{KRFY(Tpv6*J?|G^ z{8f(dGcZeSqiV}=wAq!Vqn~A4DOTU`;C^;wr#+0tjq zOm^;os+BU$ z3tCE*$&uDa5ra?DsI}kS4W6Fczvjx*dxqc0urfgOqmb4=HxAV|KPPlJOj@O|Pi-L7 z0ch;x`RmW$=x|NV`5cpNwNGlGdW3eo@Vw8Y@7K4SDa*spvJw60>V7kunEhvQNm_-- z>7od9U%+i|Xhqw{PTdQs9JJr_rqTuys(NO3pjoDU8*V$TpCNKj?j7nVi~Ladj4*)T zhH)Io8<9J*IGggivi&t~cWvxiHZe+c>ASR#7Pu{bC`_MoP(}+c2%whIhbV;{g5s64 z>x0%LaPS3{kwlwuU*pJYnj-_f%w-FA&CYPW00eY3XN3ZV5Pdt3igpPOlMeWFFUnOJlMo^DM*YU&}k4}MXhQe+9W#P7+MC!t1NHme3ya*l+ zelio1r~;24Js7*@3I~%E!_}$XSTVtWFwS329T9QpHT#BD-|D^(l7!--XkP8nuq%V7 zhpwY_Cuw$XTfs1;uEgWAM+Xe@xuu1RqWTJ<&uKrnX;_Q$FO^k$ZTBEkXw!8X`R!ZO}QzLl7isu-ggaXY)73Tqb3N@jF8)^>F(P`V=G?3J7t#niE?m!5*>Y~g-VanLdN>_e<0H9Mx!tBgkBPj2dHg* z;R(W&h9is#hqn9&qVXTQ%dsw~eq7u;JS9{_W{)f+UfI(5olayzJ!$u~jTAT(I36Bw_K#4RS`g+Ldn0#WiF_$t#lzv|pgogWwrRS;4_*(~rpp3Shr6f+OIn zpe7xyQo-OT-E&6ET1{CJ^1^(UzJt?N!7w>h2Jx;X&uYt2!${aueE&~GftA_?w?_+h zkYXuC=`V_{@&~NYZMJjs`Y0PNkpw}jA$~>E8m?8p00>kcbe`!TULmICFsj41iFZ*m zi!%tqR;&%+_FZTLT^w7~! zu!wzM6~cE-!CWk@pNF->A!@SnSr08CL0U)&!rC5(W!iO;te^DdfZ-L%sJN&OQFktEx>A(4Bn zF(O=74=0QaZP6knv8i_3Zk9cCW45aISh)mPtHSzmBdD(tBP7lkF({kH({;MXz((Vq zNbWCR5qfi(xO+bU++j|9P~yu-uz=Nuj16@J4WWVd9!|H(F5hFf>Nya^)oWT#>K3v8 z5JT%}C++&Dp=~Fi)bd=mM;x|E`?edsL5#y0Jp}ro{gN0^huTXxEi9exCyc5`Dfu7r{2+NzyQDb-=co6{xlyCf`yjkrnsdwqeTUr`$ zEL67nH%EgDH8Ij5mC6)zn#?nl;Pcv<|5~WWorN}`SKi!P=nI*w8GCJ#?6Ky8?TO}M zU*q0Gtg6S&R=`DceRkz`wi2fHK1%ufiI=%dnxzn?Or~+@u13Cf^%ylxFod5f`Fc`L z?MW@fVB&>NOf@4|E>E6th9a_yVg8+5_4wssZ&6SX8H7+WxuvDd4jTy+S@S@6HnGfi zZWkpKT&<8Buzf5t3gh#4*T_VpFR74@eQnXd2u3U>-m42JoT{7-WZcuy4iF?gv8pC$ z>#U}+{#hz!Q~BW0=gF$7YB3C|;)L|Qp^g(Mt?v<9+ta)pVWBHuQk(00Yzl}7PXI?V znEm&HprYlLE;SAww7D>=M$(UTdq*7dU7oO`i`YV+95cxnM8&S;^;+Yrf}`)IwPO_< z`u100t#9BIRu;@{w_j&E3QIT^ohC!T@CYO?Ld(-kwi9Ly3EA~X&?Cy7w2lUgSg|5A zAWi);qR(oTa3>FyBBf%+PGd(0dQotN#riz2(+ghp3KNsb7E}Wo!P6j^laSU9e>LA`q}>N8=u;pZ{0FYhqrh|+7)WcBfZzu0-g_DP8ZOs zL-ZqXK$Lo#?hY?YcNq1?yw(lAE==-^j(2$&(2M;RE8QPm&lmtQ-rXiBjJr?Iq6m`T z&%-t3m1PM`-~@ei`cno9agXe(HHx$l4KC+e*k5?9v%#*c(6P2f`G4`bIvj1!Wz3dS zYQ467>49%FRZ1KI^nrV=bka6NW$4F{Clqpj%(g;=}7H-ms^VuokDrh-3A*Gg*cAomtE;F*y5fnM z%KY9?EJ6`jx5Wq_)jU?EnUF!u9W)2Rxd;2}6j9xrKsWj`BKqm^p^p5{2ZV zVM^lVPgNn>#8?m@7J?-n%fjMO_N4Km47cX9qLslfdgAwkJ5;qe%$$oNm!zxa`|*Vc z&0N;E27z?c(;KN_6e{ntq@Eq53=NVB;E_e7C#;~8MMX;^L8COAZ7+yl*T$wyCM3Hg ze3f5_ z>fbFlvcAJjCiU}i?lae9a-<@JK$;=7fgbjsxv9DQ>vd$)num)0WjV3_A!ruy@}-Uq z_D$!?-kWtv2C2x* zK~=`ArEqd}BrJDHY#-}AROBa7oTTG$Ju$q5mtvNmk-nLc(HONo)1E$esQpgB6A*|Z zEM6BM8*@@EDi1CmppnImNL(=b*;pmBj=hlYBeA;l;Rz4vRwQN3YCStwI8{%~1u^X6 zFE9E3+*@zt{X5Q?3Rvq(iq0;%d{-=740UP{rlLMxpS8May^__h4zqA;ZZH*!yM8q#|OykRQQ6%NZCf?i|g}GS_7?7 z-QBi+)Vz(NYATBC+J2j5l_JZDZW+Vt7bN|JQ0`?k(BlF7b~>I z7xGA35Lf+_8Z;avv40>%^-Jo0%XK0&_0MjW_Mft`|HneX0Rljqouy|i8yrb>QMCyHzuo!msmvgg$^VbSS3E4ZrW4qiJpIzj)iGqd7+e}Hs*OQ%Xh8`TK zs0vo$e;&=BKlk##=zjYktMM6Wz+&Hg7h`#1Xo=G+-=QC8zKjV0>itc-w{MF&Y{dj| z{Lrh6omBd9U)J=>`PMW+PK z*63Af{=M4mf0zes?9QcmoxWQ6bN2!5wiHlJ6o^6w3wwaur}bnZ5Jqi>5W4;)~jGxlnFX~#1AI-!W&FF9tz4%B+cw#+@!_-+)4aNxx63RySWfsgRxwxj%Y6C8g1d z7R_B{e>BuFGb_G_HNm^>j8I&#O_V0T<=YZ3Hu6R>UpnJfD`=ICL; zPyxoPoo3DLs%@u6+sfH$Ii==Hmks>?t=K*2n|KHMuH-A+moR|?hUw=Iloa?OP}|&w z?N$})1P>0}f!j>I+d<+VF2H+RlBMb<@O*bnnT*`^fb$a1{_7fS-4ZeOyKVrK6I6HI z06d8VXHLD4)*%t|$0TjeMJ0>D(QNCBH4zP@O}H*YXPdVg5<)~sghHba-6fCi?u7sT z_5=VUyP5UV@&5DKQw%YjT4wx_o2{9L0QE%tdZJj!yb~9%1DjU=r!`|IB6r>%!&{p2 zOM?Cifxe1cghjY$lYMr*X}(%1ldve&wq1Sf*^pxv_t}J)Y=)iCp&=cZvo*VfD7G5E zWsEU6l9}vL#uII9A;{}D)E%4KlFQ)>9*_H>FtD~l-_qAA@)MNewaEbUnTf)88QOpS z>F?E(ou|ZDe=#b8^fU7>Y@&%VIBLzkRll;cnxgw%iPh#6duD-N$RNH9z65RQ5}!z{ z2|S<@Tu!I~h^Yz_Z7Blb?&bE}m1VP5M*nd8;lRl1M8KdNZ~C3OFPubNTCC)5=M?8! zJu41R#`k7hxJhGdQKp)$>y1@t-LVN(cN)__^8IPhb-#`Q)puwwN5H?0&|m{7q3g0n zC$eAFD#N@}M-u`53$>z!nLE4D4g!X&}Okg}72Sj3{l8U-G!!k5m2Y1>xM>H&z zj^3hy4TO#cCS13rJ0CESw&p>tJM2+<(I|ENfi3>#bWUU4ah*1ADIse=gx*AJk(-!c zH}^IV?GYoMxW32#XSU_MWL9REqF$8d&3`kgJD+lvdM7B^l_>n%`?Y~6z&+0ncvo0F zh;i6~dZv_!a674#JxBF2K2m@qAW&SU4j5?(ig!+k*<@%k{>}ADZ`I7Vspf(?kJcd) zic=omH%0`7GIGO78_(ckwBdnIA;_=BQ}nxw1yjGpkPIyG5x7*T#~BSG>6O$jTo+?f zoX$2;OHQBJUdGw++nju9c!dmtd5w5ENW6;vKj&oL4!Ar$bxkgO{@COm52B8)6PgP( z?dtWypmp*LHAaK+)o zYel-S>@hoSl5>`Nu;fKD$E^AOOWqE=h4U>9u(TX_hN5pgJV!Ao9Rd{GsJYsIjB9Ke z>#nt>(r{FBmHm`9qy0bv?`ve+uy;xz8n1_rXJI0Z>(=7*SnyfzVa`27|GxnE_6};e z75_k9Vmx5<(z3}KTh-U+bBy-??-Cxq(%G@ z-kM>BpEIaJu-npUZM5PdkVe@*S9sHZ0`_dEtvB{DM}SBlP86~T^O>yAYW4I@J&=vo zV}CXGun3mI8nKh{8_a9w9RJ>ujf=axyNq8~M)lIeE^nelM1^XS3Ftd@*Dd#dg9Si3 zR{*5rp}bVn{mY*z(7~jsbC}ZjhO@@197W7@N5m+OE)z{#NIGUr9<5kZG4D5;#mMw2 zAHkyh+~1g)IAMhZx(n4C9fCgL0yX?c-d(6x;|OHb^sYl+hL>W~N$l0qjBex+YAO2W zvizkess)2KXk8g-9hYBdvt`B8Q$`y%GiACtuDi{Lh@Gu&N;)M?KqwnH1#G;lMhdd( zbEC&r=f6|SHvjE8_XmLc|1-;Ujt3}O;ncJ)If3&BhGI3_TVCSN<`nQHjQxDBW75RctKu-FcVp)+FFt>di+XIDdreN2F`m;s8%=v+20 zJ@lG5D7NJ)nh+&l5pb&SIe-x_w65#Z2>He=qMh=F1>=UbXx^Ik_op8u+O^gFBEj>u zoLaXs?S%e6OATmGc=Rt{=>ApcDKI(4uJK4`ptIFI>kGChtqzp-K`?p380W4{_0@_{ zB4b$-a^C%U3l9%-zE3#2QH}KhyZ~q$Oh6WiMo%p3p-@d-;p;bFXQe0aZ-S(}!ZOdZ zrSy3(fea#AW!h3g!1b==BhYonR6MSX!CA0uefG%`8#0_=eQ$7&3lS6YCcV%C_adi~BhboJ_in z>);sKd@|YWJ%Zj+MNmG~Qd9d`qoY+O*wiG^9Azqm7+F+f?(j{M#}JtilU1=fae0r(U1JOHZsO<#gcIeW6gq z{FesXC2%}{z(PuA*ZPa4FN}v?{H@6~0^Jh~q$gaH3QnZF%W+Pzl0^#Pb5^4C;F;>n zh)$WJw%l-Bq<<(-QDF3RttshG4G<1RL$#)X=4ZUV3bhNuf{qY0WU0ulND`hcbWE~` zW)AK2U71f;^u!f-(o6Lo1N8xg3^rA{dnY|}CEh`;CJquLc`;)IhMM)?>qcT_UIw}m zbwGuKp~}@u2_vIk|L2$jlHa@5JIn~`cz;j9usk57p!k$m6|KFSMoSr>KV0ZCrrIzt zftSdoi_b5qMx4BX|8YMGK?J4iPvJAl49|DpG2f;eB8!YMyS%c^#JByll}ltb^wXdK z7o;lVhgxd-JzQFMfY|at%R5%;1iL~OSi zj9nz#^{XE4lsK3!zbWC)-}gcyWJ3~ck{0Z*QB^fTQ($=DzLo-Bm0tfeJu;Ek8v|%F2LO6V8L(e)8kU;3f_a2`mR}(wC-CA4GaNo zK;N{@`-;oIkPciC!2I_Qe*PuXU@r-bhIstZf@v|MJp`z-#vx@QDr4XsNIA9Mn*mIg zMZ>}#_Q9$=RJ+Y1=g*U%RSBi0QTrhO(@Tg72O0C13@?1)#h&{y@en_Gz5Ur6}IF{rVgcN(11X0w^CDrgegZo#>{hv4x zDh+Ic+LsN7f1?H_lxd}3xE30xTjgPXj@neO`>HY(65-!l%NmmmOR*z{wF&cpXhxDB z10=hFB@nmiG?ekPrwaP%hyCOl1s*NSu`R;B=_t^~&(sM-hOecX-b>7msNCB%qZKNq zo=AtDXe9`|_0A5Z35M2`gG9jb226$CSCU!JO7RvJ@$$_rRpe~G%~*_(uX|ccPR|dB zv{Cg7(3?5A&WsIt3c3{INo*+=-YXXVUcYImz=^h$(jWMHqF+eEjwxXxp}hHu(ch-N zb!J(v_`=OS*_m9DqaBD9I?=I)@W-)U0s6}N!L8U1wF*N?52R)Rb|Zhk%#k9y zM2*L!9e%f7r5ym;{LXlp5osM4>=e|)%qIhfX06HP!L9O;g#|S&RsD9J2QtiX0O#36 zG5HPiWK)vM%)XuucG7KYM)6#(a`IiLJ6#h$;I|5>Hkc}9;`nM!w3%a?`_`Vx-P08v z`O07}Bu?h}@vgkh_kY3Kd+SjK-avr9XenR@$~=F|HzsJgC1bgh42o?&NsdgiJg+iV z{3$hbAn(xK#8XdVwDdm2X8`EHYM~BvU>%)X>wzGj4shiF zcsp*?=Mxb@D+!|Zcb)EK@t=~@%Nd#sG7QUVwSExCHA@*QwauZh*h4HFv4N&%p>pcz zi7&;bg5}h)T07f%iqu7(P36BY*tDIZu~iDoSldm{tNfET{z>67@dufoWNvd87X7)G z&q81yHH501eFjok%*0y?t}3_Pf}iGXi?C@HfK{eAmc6*U(mLJ%jVvmzB3ch@XcMY3 z4H`BqTK)XaBlx^2FFWGBH55+!?9JidJteN#c4skBX&1A~nKAIg$RGd?qo!5HAS@Ii zNJR(cd)Kux?*s!VSlVe{oZAPaQa_D4<%3Mn9tm2hRBB9=;noTFoHgO?mqRltmmRPN zyLn){UxM12FRyMLcKb7yM2oKwn#U1MXc6ObH2yizSQ_77rb`%Cf2#g2^{TYn&3@JS z+w%`kdmfIpFAZ5ll(E=rQ8Z>zSkbJ#A++m$_Ya|5Ana1S(ApUNLNrg`+^1Q5cfSh* zOe5)!fQW>>qzcq1qJAIPvS|c?{$>|c^_3?hCGd+~O=bErIl&%$; zf|$`*g)N#$$52D2pt}+$PKI_ww{?~`~-#i3sYltB;|zWs}1NhG!iMe z6?QPdH}g03=iRie_XRVRX_#Z2Nff9wD@CYW6<*A|jWy*aOW2+X!?#U)Urt5|;B;3V5P-lbv2U$2f5#_D~r zYx9hE9_Y8LF0DZ58+O>TAYvk zK2)Dif5kto6b!W%bB|eGJk~Vbzi?J^)QeQ5KXtI}gbP(ViFvp!J6T*@Khix3x^?Fy z8&EZs$cTJR4R`IdhcqS*bO+rS3(V|u6GK%V9fc35nBPV>FH$Ai8BEn&vu~l~ z$ex($q^GW&Uo*nEXXdqJj6#-qg@e>#GiSJb>0^V1J|i zV;f+ay?3a=lw7rlH==j80=818;p(IA2pKB(>^OCh@R9h8nAqERV*qig(DSWw<2AUJ zpdO}*WD;vyz<3u=RV?pWLIsz)PV%@>Z}9Tfvxg5!HiE>&B|Kk-@|-&nH|fKUa3pSJ z#1jHKq_1Xbll#YY-ad>@p6v=*981)!GhY%KDNWpko5BBiW5s z(!rNv3gh?H)uRHYdGJ@~@R6w%U%^$j%@{h#lnlzGCn8NZZ4yVk`_&}n0{ET{`0yC> zlKq_#%^tusS9DMynoBnJ#~91A$9VTZEDR&OEvjBCt|HJA4&|OBu&MU@(Q(B zo51l90aKN(gmFse;~r~>SQTkJB?Zp}&+S?)ecQw8UXIuR`JDw%@TKcPO+~ttvUB@Q z^mLn(=tI1v!x&}}`5A^G!3@(FnMx9Hfxi7eF&V%8%J20Ka3S6^{J4g_Gw|2g*<%$W zPw`HBrT)M6uKk_q{{P>JJKegI>NbZNQO+hh*mABryGf+TncC3FX)DYjyO9vK_L*ap z?t4YK%i-?8a!$6{%92Pqn>lA~Vwl6{T~_hAzJI~@;-~kuYp&Oxujlh|dcAk>D94qQ zDH)p%GWFSsxr$@&7_BgcbK!x1y=>x~{$tuK$~9QM67z7oBwXz_H#|NV2Cp}+8TJRuZ`-LW6|Rf z0+nD1ao8~pbimw$aqzE)s5`5JQi`$%CD>f4PUOK>& zk{=3CaK3{_R_e|>w(RQa^5bpD9id)VSQQ=K;uxA$x8Y;huMDHM{YT89eq7pHYIGE| zV2>>Fh8n28`0%|_1$@v82N$M6V=1NbgM_-XtZaqazl(hLs<3QvJGFas9EL`1{#>5Z zDcA6piWZkoFwjaj_B12xV#Di7^D5v-21+h3hdN*%=F#e_-GKU`>%H@Y*9SbpaD*;w|Qr=9CHzQ5GhkN+$o zk4)<ogZ(t2_WD^fg+!rbx}mM+_XE>vZmu zjX5kXCx7~7(~Ip0Ta5qjyZ7RgJ1Jz8DgI~XVPKPZutAkjc?rHt;U-N0ViSFGw4F4V zI^2rKJ8OtPJe9z)1bb(A_b5X`jbRuB?VR8uVaX^&er!7HhZ_MD#}~k0=04z^_+O{P ztdnf9>W+63Ma2&?9=5ZH zWf~-KEWX&-GoLkJ-~_KD0-%znRn7D?450^rpRoal1H!GayfrjFW=Zh76t^?w3J+Mj_QS;L{0lZ<22+R*@Y>5OT0YL4v`1 zBF&LwzRDfkbFIZKyzBIQvJ3-;#4~2uo-@9}U?W*w<+P4RlN>>qxwiX1r7ZTtyh|0D zi__E&&taa=HQ+m5I8NHHl}_x-e(^Vt#82oF`eIGAaGjA`lBpX2x)`alafs}@RGKu}K65@n! zduf-sW)-glya{zfOC8o<0L8dMR7)IgeJyw?ti zRO7-Mi7ZxbKV1pDq4Xl#!8SO>4q_kd(_RYN25y;wYy<0)QH$y;I2_~&@L#j>08cb^ zEZyq}_bF5+Eg0*vh``n8HtDC8d0(#ZaQD(%n(eC5x&Sz+kaG=pXnUlDb!UE~s}=GW zT}|}4R8!Ekq0RVa*um7eF4)u2fn+7{T`=wOasM8yNu5lP8u?KI+qG{2ykhZIVB!k{ z9zI`o-7zMk8cjm2(n1QDs?!fr9dA$VRr7W-mm@I8-`l$4S)4|5sZyp|@s0`^tc)1t zqFUY=iiJr546j-Xy8+3J9`A!CRw*XPgpT9qeC2`!Z1@MIqf-IKFg}PPDo@m6XYnga z-hH3eEx(pyk1QbTKJT7Z8J`Me;t0b2dE{*4D|g>XM#Sv9cRl0ya<{B)5pUgxPE#d>z~Q!%0@d z0E6~#XcJp~nHj6gECko)u$+0UU;byp&%V*o)w&JDll798-S68|vi@haasvSPpWUK$<#Rm@N zE0$&*vH(75zI|b{2wulG4m5DG%hgRy;abY(l_wS-KRRaCQSID@#LoZhz-6NLuee}Gd2>?CPP$%S41XV zPhkn_>ivWJEdXKzuzjnW5w>;NHawAh(K<3Q^||0o7y95Hg9ZaRd&Y5HU7YWNo9)*8n;~iBtkHJkb{!fJ zc1}m>#a-WIVy7wzPx|8c=Og=!L{%<}axsu&aC-fy<$b>mkI^^*DL@k=3UIGm%@dQ~ z3VAjQuV8OR+o;yA+1_WH4%Y#0ddPaBpUy4}rIntFGK7X9c{8UBwT+Dl5apxkL*(q< z8SPxc$|{351JUlH#k7V9M@)^~NzQ{qVk6QMIJdb4FG^{(yx@Ybp1MhQMQGJa*30w; z3We#Mg_yr7M)5H@l)6B6c&{v7p`>U`uE!c{RYliqQj?67lcEv!d`<*Jw%Ncm$BMdx zGh^q`^Ig<|ix|*Om2s?PNwnVbtOZjfsJz03N&)H(yS}}ZH`n&6yrxZ0uSPzZ={yLE zSIVqt=$RYNvrUlkaQ^8hEvohJmX1LtiGQ+5F0k(8*X;V6EL1V`_V~odwA`QO+Q|0@ zlzK)-rIt~o8 z*O`udX{CaUndO`p_}pMN*fZ-->rV&ergdYt3r@>hl^di81pT*2N+gpv{_Xmtkvq9m zb9EThInUH>xoKY&`ooqjp^ux-IEk8T{=16o2>}}!*3D%#WtdNN>b!F}JxLonslPb_Z^QaR1MTeTGV6GjdCh9KuMIa%4_Ab9=tT-(BWf}7R ztj87QK+qwT^uN7*1U4kEE? zSu0@Ky*AYeB3b?vU+Y@^!5o?;K*aGaJBNlqjD$#OM#L3JveXgZ3%*Z0QUycE7)l2d zL~l&l1hbOyi>h3@_gRfjUr-b_S9I1Gn3+XX;@FMhzWPf$BE?s>QRUMnppR!PCe=ib z#%PGiN9Vq%PVn%H*2h~gJf)K_KI#&i9COAzM?HPXYJ&HVZfuu|%J~{qf=%$A;4{8* zrtl2Z?gcBsF5NjDRGL(r5?j~cg=va(06LXx}E5-vknb3T9c%nxbipTpr)ClX!Ttie2rz(db%jivV(&RR?iHYQXTqmelS%zT@n4b6&V#stvktj`xpDut9W)E zb^EJ=sxFKi?ds*qi@{{HFi zia8+84)1%kFE&{mG0{Ue1?K^wqAF`(7(qwN(N1tMymMDn>_dYuZ3BDz_m>}w$L?M= z+`-T(l%`1@_u|Z zBl1WY&FZeLYUdw0fkdp!6bgv~8!vnK`>FN2$7n>SIO~aCIa-+6Y-7gbw<`h!P!=8aPNqj(8nc<%vz+2-#!fu?4d;;>$$)L<& zp|RJ~sG24!E-leO5oqS4r+SYrouHwba~QWq55jDlDkN_}O{zHn6AU*2n`GcsN8D zvcKi<@A)t!!3$nzaLp4O zX;4R;%IPg1?wXoeWdTYhpo_SD-u~uM7-KM7WhS_*1h~5R!7iJh68RiYEJ0h>KHN{fMz*j@0Y{bX4S2PpKD#+2} zn)2xjS&k$4{82xr>OP{>f$DV)1uJW}0MgeLC3_u?@Y|z9JA*T5?l*JO7GR?zxFw!| zV*?JDTAILZrMc;4;sn$7%!sJ@={Y~65wxB3hz?F2$S%!}Uo0N*7Po{AT|CFtw*EBO zJ00-#y52a<%U0*;Z0T<+Eu+5nB47_pHvC=_E25uI9ui2#j`f7bkvrpqNN(2P@XZwF zhc`CiL1TVbHni{=jPtJ7Od-kM<9safA_QQ#psxN;S-o;Eic)iXv9QsQNSGP-V@6~` zqW>-C;tJyKKD$X$J zlkb)jits)IyjB|8_TqpcCYhS&ykTB+FGK{n88d+8xMWV`H?h;V?A2A@l`o@d%e1B# z$!ErAeTF3o2h*SMC!5E7pJCl(F?qC3kLEzWP*iZg1jGCX7Xe~Pt*vY`E+XnndczS zdYM+&teFoVWQ_OQ$ltn^R%W!HX#>6xqR=yWsVqw&X+Z@cb9)?*s>9miJCk02x&L(Y zCW1i7zmarP$zC{iYl1KbUDaO|V3MvVvC>=gMT9Dun`cq9j^*1658>S_^Zc1yZ>aPu za0Y#Re9}cvic)KWuW-xC(|=UTLWTN0m9n%`>x8A|$E+%@BZOH=^Fh_lMAW^0P2fy$4p6ukXs3FY=xEt>^s;Wi%-gV6nR%691Hc)9$kN}P zXz%RC-(l#jClp&?ZKJyRd(=xWprB&cb0_FYc$0qZ9>`G*&feJnUMwfu zRWtYF8HHQ7ggUm`HT<~4BRUmd=7yMQpt6rs9t}VKV-;!K)&>rIoIW~kT4|V8JY#M7 zwas4}z15Ii_8M!zTce#d()~YVYfW<21b5ADta4!@bG2r+)~x25(O!`*zJp{fW~{}G zwV1IMGrkjo=wdBot%a<$khK=F{>L+{Ma;E`xfU`1S1iAPWG!N@Ma;E``9E^;_4s8i hV*Y=Qm@!Mzul(=KY@t_%`~>`2AGZ6w=-{c?{{ox~Lq`Ar literal 0 HcmV?d00001 diff --git a/Sources/BedrockService/Docs.docc/Streaming.md b/Sources/BedrockService/Docs.docc/Streaming.md new file mode 100644 index 00000000..3051c883 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Streaming.md @@ -0,0 +1,103 @@ +# Streaming + +Get real-time responses with streaming + +## Overview + +Streaming allows you to receive model responses in real-time as they're generated, providing a better user experience for interactive applications. + +## Basic Streaming + +Use the same `ConverseRequestBuilder` with `converseStream`: + +```swift +let model: BedrockModel = .nova_lite + +guard model.hasConverseModality(.streaming) else { + throw MyError.incorrectModality("\(model.name) does not support streaming") +} + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Tell me about rainbows") + +let reply = try await bedrock.converseStream(with: builder) + +for try await element in reply.stream { + switch element { + case .messageStart(let role): + print("Message started with role: \(role)") + + case .text(_, let text): + print(text, terminator: "") + + case .messageComplete(_): + print("\n") + + case .metaData(let metaData): + print("Metadata: \(metaData)") + + default: + break + } +} +``` + +## Stream Elements + +The stream provides different types of elements: + +- `.messageStart(Role)` - Beginning of a message +- `.text(Int, String)` - Partial text content with index +- `.reasoning(Int, String)` - Partial reasoning content with index +- `.toolUse(Int, ToolUseBlock)` - Complete tool use response +- `.messageComplete(Message)` - Complete message with all content +- `.metaData(ResponseMetadata)` - Response metadata including token usage + +## Interactive Chat Loop + +Build an interactive chat application: + +```swift +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("Introduce yourself") + +while true { + let stream = try await bedrock.converseStream(with: builder) + var assistantMessage: Message = Message("empty") + + for try await element in stream { + switch element { + case .text(_, let text): + print(text, terminator: "") + + case .messageComplete(let message): + assistantMessage = message + print("\n") + + default: + break + } + } + + print("You: ") + guard let prompt = readLine(), prompt != "quit" else { break } + + builder = try ConverseRequestBuilder(from: builder, with: assistantMessage) + .withPrompt(prompt) +} +``` + +## Low-Level Stream Access + +For maximum control, access the raw AWS SDK stream: + +```swift +let reply = try await bedrock.converseStream(with: builder) +// Access reply.rawStream for the low-level AWS SDK stream +``` + +## See Also + +- +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/TextGeneration.md b/Sources/BedrockService/Docs.docc/TextGeneration.md new file mode 100644 index 00000000..45fa2439 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/TextGeneration.md @@ -0,0 +1,167 @@ +# Text Generation + +Generate text using the InvokeModel API + +## Overview + +The InvokeModel API provides direct access to foundation models for text completion tasks. This is useful for simple text generation without the conversational context of the Converse API. + +## Basic Text Generation + +Generate text from a prompt: + +```swift +let model: BedrockModel = .nova_micro + +guard model.hasTextModality() else { + throw MyError.incorrectModality("\(model.name) does not support text generation") +} + +let textCompletion = try await bedrock.completeText( + "Write a story about a space adventure", + with: model +) + +print(textCompletion.completion) +``` + +## Generation Parameters + +Control text generation behavior: + +```swift +let textCompletion = try await bedrock.completeText( + "Explain quantum computing in simple terms", + with: model, + maxTokens: 1000, + temperature: 0.7, + topP: 0.9, + topK: 250, + stopSequences: ["THE END", "CONCLUSION"] +) + +print(textCompletion.completion) +``` + +### Parameter Descriptions + +- **maxTokens**: Maximum number of tokens to generate +- **temperature**: Controls randomness (0.0 = deterministic, 1.0 = very random) +- **topP**: Nucleus sampling threshold (0.0-1.0) +- **topK**: Limits vocabulary to top K tokens +- **stopSequences**: Strings that stop generation when encountered + +## Model-Specific Parameters + +Different models support different parameters: + +```swift +// Check what parameters a model supports +if let textModality = model.modality as? TextModality { + let params = textModality.getParameters() + + if params.temperature.isSupported { + print("Temperature range: \(params.temperature.minValue)-\(params.temperature.maxValue ?? 1.0)") + } + + if params.topK.isSupported { + print("TopK supported with max: \(params.topK.maxValue ?? "unlimited")") + } else { + print("TopK not supported by this model") + } +} +``` + +## Use Cases + +### Creative Writing +```swift +let story = try await bedrock.completeText( + "Once upon a time in a magical forest", + with: model, + temperature: 0.9, // High creativity + maxTokens: 500 +) +``` + +### Technical Documentation +```swift +let documentation = try await bedrock.completeText( + "## API Reference\n\nThe authenticate() function", + with: model, + temperature: 0.3, // Low creativity, more factual + maxTokens: 800 +) +``` + +### Code Generation +```swift +let code = try await bedrock.completeText( + "// Swift function to calculate fibonacci numbers\nfunc fibonacci(", + with: model, + temperature: 0.1, // Very deterministic + stopSequences: ["\n\n", "// End"] +) +``` + +### Structured Output +```swift +let jsonOutput = try await bedrock.completeText( + "Generate a JSON object representing a user profile:", + with: model, + temperature: 0.2, + stopSequences: ["}"], + maxTokens: 200 +) +``` + +## Error Handling + +Handle parameter validation and model errors: + +```swift +do { + let completion = try await bedrock.completeText( + "Generate text", + with: model, + temperature: 2.0 // Invalid temperature + ) +} catch BedrockServiceError.parameterOutOfRange(let param, let value, let range) { + print("Parameter \(param) value \(value) outside range: \(range)") +} catch BedrockServiceError.notSupported(let feature) { + print("Feature not supported: \(feature)") +} catch { + print("Text generation failed: \(error)") +} +``` + +## Comparing with Converse API + +| Feature | InvokeModel (Text) | Converse API | +|---------|-------------------|--------------| +| Conversation history | ❌ | ✅ | +| System prompts | ❌ | ✅ | +| Tool calling | ❌ | ✅ | +| Vision support | ❌ | ✅ | +| Streaming | ❌ | ✅ | +| Simple text completion | ✅ | ✅ | +| Lower latency | ✅ | ❌ | + +## When to Use Text Generation + +Use the InvokeModel text generation API when you need: +- Simple, one-shot text completion +- Lower latency responses +- Direct control over model parameters +- No conversation context required + +Use the Converse API when you need: +- Multi-turn conversations +- System prompts and instructions +- Tool calling capabilities +- Vision or document processing + +## See Also + +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Tools.md b/Sources/BedrockService/Docs.docc/Tools.md new file mode 100644 index 00000000..cb017b76 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Tools.md @@ -0,0 +1,215 @@ +# Tools + +Enable function calling with foundation models + +## Overview + +Tools allow foundation models to call external functions, enabling them to access real-time data, perform calculations, and interact with external systems. + +## Basic Tool Usage + +Define and use a simple tool: + +```swift +let model: BedrockModel = .nova_lite + +guard model.hasConverseModality(.toolUse) else { + throw MyError.incorrectModality("\(model.name) does not support tools") +} + +// Define the tool's input schema +let inputSchema = JSON([ + "type": "object", + "properties": [ + "sign": [ + "type": "string", + "description": "Radio station call sign (e.g., WZPZ, WKRP)" + ] + ], + "required": ["sign"] +]) + +// Create the tool +let tool = try Tool( + name: "top_song", + inputSchema: inputSchema, + description: "Get the most popular song on a radio station" +) + +// Use the tool in a conversation +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("What is the most popular song on WZPZ?") + .withTool(tool) + +var reply = try await bedrock.converse(with: builder) + +// Handle tool use request +if let toolUse = try? reply.getToolUse() { + let sign: String? = toolUse.input["sign"] + let result = getMostPopularSong(for: sign ?? "") + + builder = try ConverseRequestBuilder(from: builder, with: reply) + .withToolResult(result) + + reply = try await bedrock.converse(with: builder) +} + +print("Assistant: \(reply)") +``` + +## Multiple Tools + +Add multiple tools to expand capabilities: + +```swift +let weatherTool = try Tool( + name: "get_weather", + inputSchema: JSON([ + "type": "object", + "properties": [ + "location": ["type": "string", "description": "City name"] + ], + "required": ["location"] + ]), + description: "Get current weather for a location" +) + +let calculatorTool = try Tool( + name: "calculate", + inputSchema: JSON([ + "type": "object", + "properties": [ + "expression": ["type": "string", "description": "Math expression to evaluate"] + ], + "required": ["expression"] + ]), + description: "Perform mathematical calculations" +) + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("What's the weather in Paris and what's 15 * 23?") + .withTools([weatherTool, calculatorTool]) +``` + +## Tool Result Types + +Return different types of data as tool results: + +```swift +// String result +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withToolResult("Sunny, 22°C") + +// JSON result +let weatherData = ["temperature": 22, "condition": "sunny"] +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withToolResult(weatherData) + +// Custom Codable type +struct WeatherInfo: Codable { + let temperature: Int + let condition: String +} + +let weather = WeatherInfo(temperature: 22, condition: "sunny") +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withToolResult(weather) +``` + +## Interactive Tool Usage + +Build an interactive system with multiple tool calls: + +```swift +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("Introduce yourself and mention your available tools") + .withTools([weatherTool, calculatorTool]) + +while true { + let reply = try await bedrock.converse(with: builder) + + if let toolUse = try? reply.getToolUse() { + let result = handleToolUse(toolUse) + builder = try ConverseRequestBuilder(from: builder, with: reply) + .withToolResult(result) + } else { + print("Assistant: \(reply)") + print("You: ") + guard let prompt = readLine(), prompt != "quit" else { break } + + builder = try ConverseRequestBuilder(from: builder, with: reply) + .withPrompt(prompt) + } +} + +func handleToolUse(_ toolUse: ToolUseBlock) -> String { + switch toolUse.name { + case "get_weather": + let location: String? = toolUse.input["location"] + return getWeather(for: location ?? "") + case "calculate": + let expression: String? = toolUse.input["expression"] + return calculate(expression ?? "") + default: + return "Unknown tool" + } +} +``` + +## Streaming with Tools + +Tools work seamlessly with streaming: + +```swift +let stream = try await bedrock.converseStream(with: builder) + +for try await element in stream { + switch element { + case .text(_, let text): + print(text, terminator: "") + case .toolUse(let index, let toolUse): + print("Tool requested: \(toolUse.name)") + case .messageComplete(let message): + // Handle tool use from complete message + break + default: + break + } +} +``` + +## JSON Schema Helper + +The `JSON` struct provides convenient schema creation: + +```swift +// From dictionary +let schema = JSON([ + "type": "object", + "properties": [ + "query": ["type": "string"] + ] +]) + +// From JSON string +let jsonString = """ +{ + "type": "object", + "properties": { + "location": {"type": "string"}, + "units": {"type": "string", "enum": ["celsius", "fahrenheit"]} + }, + "required": ["location"] +} +""" +let schema = try JSON(from: jsonString) + +// Access values +let location: String? = toolUse.input["location"] +let units: String? = toolUse.input["units"] +``` + +## See Also + +- +- \ No newline at end of file diff --git a/Sources/BedrockService/Docs.docc/Vision.md b/Sources/BedrockService/Docs.docc/Vision.md new file mode 100644 index 00000000..5a863c81 --- /dev/null +++ b/Sources/BedrockService/Docs.docc/Vision.md @@ -0,0 +1,109 @@ +# Vision + +Add images to your conversations + +## Overview + +Vision capabilities allow you to send images to foundation models for analysis, description, and question answering about visual content. + +## Basic Image Analysis + +Send an image with your prompt: + +```swift +let model: BedrockModel = .nova_lite + +guard model.hasConverseModality(.vision) else { + throw MyError.incorrectModality("\(model.name) does not support vision") +} + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Can you tell me about this plant?") + .withImage(format: .jpeg, source: base64EncodedImage) + +let reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") +``` + +## Supported Image Formats + +BedrockService supports common image formats: +- JPEG (`.jpeg`) +- PNG (`.png`) +- GIF (`.gif`) +- WebP (`.webp`) + +## Image with Parameters + +Combine images with inference parameters: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Describe this image in detail") + .withImage(format: .jpeg, source: base64EncodedImage) + .withTemperature(0.8) + .withMaxTokens(1000) + +let reply = try await bedrock.converse(with: builder) +``` + +## Multi-turn Vision Conversations + +Continue conversations that include images: + +```swift +var builder = try ConverseRequestBuilder(with: model) + .withPrompt("What type of flower is this?") + .withImage(format: .jpeg, source: base64EncodedImage) + +var reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") + +// Continue without sending the image again +builder = try ConverseRequestBuilder(from: builder, with: reply) + .withPrompt("Where can I find those flowers?") + +reply = try await bedrock.converse(with: builder) +print("Assistant: \(reply)") +``` + +## Using ImageBlock + +For more control, create `ImageBlock` objects directly: + +```swift +let imageBlock = ImageBlock(format: .jpeg, source: base64EncodedImage) + +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Analyze this image") + .withImage(imageBlock) +``` + +## Streaming with Vision + +Vision works seamlessly with streaming: + +```swift +let builder = try ConverseRequestBuilder(with: model) + .withPrompt("Describe what you see in this image") + .withImage(format: .png, source: base64EncodedImage) + +let stream = try await bedrock.converseStream(with: builder) + +for try await element in stream { + switch element { + case .text(_, let text): + print(text, terminator: "") + case .messageComplete(_): + print("\n") + default: + break + } +} +``` + +## See Also + +- +- +- \ No newline at end of file diff --git a/Sources/InvokeModel/BedrockService+ImageParameterValidation.swift b/Sources/BedrockService/InvokeModel/BedrockService+ImageParameterValidation.swift similarity index 91% rename from Sources/InvokeModel/BedrockService+ImageParameterValidation.swift rename to Sources/BedrockService/InvokeModel/BedrockService+ImageParameterValidation.swift index 78182e13..738f9b85 100644 --- a/Sources/InvokeModel/BedrockService+ImageParameterValidation.swift +++ b/Sources/BedrockService/InvokeModel/BedrockService+ImageParameterValidation.swift @@ -65,7 +65,11 @@ extension BedrockService { /// Validates parameters for text-to-image generation requests /// - Parameters: - /// - modality: The text-to-image modality of the model to use + /// - model: The text-to-image model to use + /// - nrOfImages: Optional number of images to generate + /// - cfgScale: Optional configuration scale parameter + /// - resolution: Optional image resolution + /// - seed: Optional seed for reproducible generation /// - prompt: The input text prompt describing the desired image /// - negativePrompt: Optional text describing what to avoid in the generated image /// - Throws: BedrockLibraryError if the parameters are invalid or exceed model constraints @@ -103,7 +107,11 @@ extension BedrockService { /// Validates image variation generation parameters /// - Parameters: - /// - modality: The image variation modality of the model + /// - model: The image variation model + /// - nrOfImages: Optional number of images to generate + /// - cfgScale: Optional configuration scale parameter + /// - resolution: Optional image resolution + /// - seed: Optional seed for reproducible generation /// - images: Array of base64 encoded images to use as reference /// - prompt: Text prompt describing desired variations /// - similarity: Optional parameter controlling variation similarity diff --git a/Sources/InvokeModel/BedrockService+InvokeModelEmbeddings.swift b/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelEmbeddings.swift similarity index 98% rename from Sources/InvokeModel/BedrockService+InvokeModelEmbeddings.swift rename to Sources/BedrockService/InvokeModel/BedrockService+InvokeModelEmbeddings.swift index 68c6546e..61e76127 100644 --- a/Sources/InvokeModel/BedrockService+InvokeModelEmbeddings.swift +++ b/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelEmbeddings.swift @@ -29,6 +29,7 @@ extension BedrockService { /// - text: The input text to generate embeddings for /// - model: The Bedrock model to use for embeddings generation /// - vectorSize: The size of the output vector (default: 1024) + /// - normalize: Whether to normalize the output vectors (default: true) /// - Returns: A TextCompletion containing the generated embeddings /// - Throws: BedrockLibraryError if the model doesn't support embeddings or if the request fails public func embed( diff --git a/Sources/InvokeModel/BedrockService+InvokeModelImage.swift b/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift similarity index 100% rename from Sources/InvokeModel/BedrockService+InvokeModelImage.swift rename to Sources/BedrockService/InvokeModel/BedrockService+InvokeModelImage.swift diff --git a/Sources/InvokeModel/BedrockService+InvokeModelText.swift b/Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift similarity index 100% rename from Sources/InvokeModel/BedrockService+InvokeModelText.swift rename to Sources/BedrockService/InvokeModel/BedrockService+InvokeModelText.swift diff --git a/Sources/InvokeModel/ContentType.swift b/Sources/BedrockService/InvokeModel/ContentType.swift similarity index 100% rename from Sources/InvokeModel/ContentType.swift rename to Sources/BedrockService/InvokeModel/ContentType.swift diff --git a/Sources/InvokeModel/Embeddings.swift b/Sources/BedrockService/InvokeModel/Embeddings.swift similarity index 100% rename from Sources/InvokeModel/Embeddings.swift rename to Sources/BedrockService/InvokeModel/Embeddings.swift diff --git a/Sources/InvokeModel/ImageGenerationOutput.swift b/Sources/BedrockService/InvokeModel/ImageGenerationOutput.swift similarity index 100% rename from Sources/InvokeModel/ImageGenerationOutput.swift rename to Sources/BedrockService/InvokeModel/ImageGenerationOutput.swift diff --git a/Sources/InvokeModel/ImageResolution.swift b/Sources/BedrockService/InvokeModel/ImageResolution.swift similarity index 100% rename from Sources/InvokeModel/ImageResolution.swift rename to Sources/BedrockService/InvokeModel/ImageResolution.swift diff --git a/Sources/InvokeModel/InvokeModelRequest.swift b/Sources/BedrockService/InvokeModel/InvokeModelRequest.swift similarity index 100% rename from Sources/InvokeModel/InvokeModelRequest.swift rename to Sources/BedrockService/InvokeModel/InvokeModelRequest.swift diff --git a/Sources/InvokeModel/InvokeModelResponse.swift b/Sources/BedrockService/InvokeModel/InvokeModelResponse.swift similarity index 100% rename from Sources/InvokeModel/InvokeModelResponse.swift rename to Sources/BedrockService/InvokeModel/InvokeModelResponse.swift diff --git a/Sources/InvokeModel/Protocols.swift b/Sources/BedrockService/InvokeModel/Protocols.swift similarity index 100% rename from Sources/InvokeModel/Protocols.swift rename to Sources/BedrockService/InvokeModel/Protocols.swift diff --git a/Sources/InvokeModel/TextCompletion.swift b/Sources/BedrockService/InvokeModel/TextCompletion.swift similarity index 100% rename from Sources/InvokeModel/TextCompletion.swift rename to Sources/BedrockService/InvokeModel/TextCompletion.swift diff --git a/Sources/ListModels/ModelSummary.swift b/Sources/BedrockService/ListModels/ModelSummary.swift similarity index 100% rename from Sources/ListModels/ModelSummary.swift rename to Sources/BedrockService/ListModels/ModelSummary.swift diff --git a/Sources/Modalities/ConverseFeature.swift b/Sources/BedrockService/Modalities/ConverseFeature.swift similarity index 100% rename from Sources/Modalities/ConverseFeature.swift rename to Sources/BedrockService/Modalities/ConverseFeature.swift diff --git a/Sources/Modalities/ConverseModality.swift b/Sources/BedrockService/Modalities/ConverseModality.swift similarity index 100% rename from Sources/Modalities/ConverseModality.swift rename to Sources/BedrockService/Modalities/ConverseModality.swift diff --git a/Sources/Modalities/CrossRegionInference.swift b/Sources/BedrockService/Modalities/CrossRegionInference.swift similarity index 100% rename from Sources/Modalities/CrossRegionInference.swift rename to Sources/BedrockService/Modalities/CrossRegionInference.swift diff --git a/Sources/Modalities/EmbeddingsModality.swift b/Sources/BedrockService/Modalities/EmbeddingsModality.swift similarity index 100% rename from Sources/Modalities/EmbeddingsModality.swift rename to Sources/BedrockService/Modalities/EmbeddingsModality.swift diff --git a/Sources/Modalities/ImageModality.swift b/Sources/BedrockService/Modalities/ImageModality.swift similarity index 100% rename from Sources/Modalities/ImageModality.swift rename to Sources/BedrockService/Modalities/ImageModality.swift diff --git a/Sources/Modalities/Modality.swift b/Sources/BedrockService/Modalities/Modality.swift similarity index 100% rename from Sources/Modalities/Modality.swift rename to Sources/BedrockService/Modalities/Modality.swift diff --git a/Sources/Modalities/StandardConverse.swift b/Sources/BedrockService/Modalities/StandardConverse.swift similarity index 100% rename from Sources/Modalities/StandardConverse.swift rename to Sources/BedrockService/Modalities/StandardConverse.swift diff --git a/Sources/Modalities/TextModality.swift b/Sources/BedrockService/Modalities/TextModality.swift similarity index 100% rename from Sources/Modalities/TextModality.swift rename to Sources/BedrockService/Modalities/TextModality.swift diff --git a/Sources/Models/Amazon/AmazonImage.swift b/Sources/BedrockService/Models/Amazon/AmazonImage.swift similarity index 100% rename from Sources/Models/Amazon/AmazonImage.swift rename to Sources/BedrockService/Models/Amazon/AmazonImage.swift diff --git a/Sources/Models/Amazon/AmazonImageRequestBody.swift b/Sources/BedrockService/Models/Amazon/AmazonImageRequestBody.swift similarity index 90% rename from Sources/Models/Amazon/AmazonImageRequestBody.swift rename to Sources/BedrockService/Models/Amazon/AmazonImageRequestBody.swift index 8f436363..ae841ff8 100644 --- a/Sources/Models/Amazon/AmazonImageRequestBody.swift +++ b/Sources/BedrockService/Models/Amazon/AmazonImageRequestBody.swift @@ -31,8 +31,12 @@ public struct AmazonImageRequestBody: BedrockBodyCodable { /// Creates a text-to-image generation request body /// - Parameters: /// - prompt: The text description of the image to generate - /// - nrOfImages: The number of images to generate /// - negativeText: The text description of what to exclude from the generated image + /// - nrOfImages: The number of images to generate + /// - cfgScale: Configuration scale parameter for generation control + /// - seed: Seed for reproducible generation + /// - quality: Quality setting for the generated image + /// - resolution: Resolution of the generated image /// - Returns: A configured AmazonImageRequestBody for text-to-image generation public static func textToImage( prompt: String, @@ -79,8 +83,12 @@ public struct AmazonImageRequestBody: BedrockBodyCodable { /// Creates a text-to-image conditioned generation request body /// - Parameters: /// - prompt: The text description of the image to generate - /// - nrOfImages: The number of images to generate /// - negativeText: The text description of what to exclude from the generated image + /// - nrOfImages: The number of images to generate + /// - cfgScale: Configuration scale parameter for generation control + /// - seed: Seed for reproducible generation + /// - quality: Quality setting for the generated image + /// - resolution: Resolution of the generated image /// - Returns: A configured AmazonImageRequestBody for text-to-image generation public static func conditionedTextToImage( prompt: String, @@ -135,10 +143,15 @@ public struct AmazonImageRequestBody: BedrockBodyCodable { /// Creates an image variation generation request /// - Parameters: + /// - referenceImages: Array of base64-encoded strings of the source images /// - prompt: The text description to guide the variation generation - /// - referenceImage: The base64-encoded string of the source image + /// - negativeText: Optional text describing what to avoid /// - similarity: How similar the variations should be to the source image (0.2-1.0) /// - nrOfImages: The number of variations to generate (default: 1) + /// - seed: Seed for reproducible generation + /// - quality: Quality setting for the generated image + /// - cfgScale: Configuration scale parameter for generation control + /// - resolution: Resolution of the generated image /// - Returns: A configured AmazonImageRequestBody for image variation generation public static func imageVariation( referenceImages: [String], @@ -200,6 +213,10 @@ public struct AmazonImageRequestBody: BedrockBodyCodable { /// - colors: A list of color codes that will be used in the image, expressed as hexadecimal values in the form “#RRGGBB”. /// - negativeText: The text description of what to exclude from the generated image /// - referenceImage: The base64-encoded string of the source image (colors in this image will also be used in the generated image) + /// - cfgScale: Configuration scale parameter for generation control + /// - seed: Seed for reproducible generation + /// - quality: Quality setting for the generated image + /// - resolution: Resolution of the generated image /// - Returns: A configured AmazonImageRequestBody for color guided image generation public static func colorGuidedGeneration( prompt: String, diff --git a/Sources/Models/Amazon/AmazonImageResponseBody.swift b/Sources/BedrockService/Models/Amazon/AmazonImageResponseBody.swift similarity index 100% rename from Sources/Models/Amazon/AmazonImageResponseBody.swift rename to Sources/BedrockService/Models/Amazon/AmazonImageResponseBody.swift diff --git a/Sources/Models/Amazon/Nova/Nova.swift b/Sources/BedrockService/Models/Amazon/Nova/Nova.swift similarity index 100% rename from Sources/Models/Amazon/Nova/Nova.swift rename to Sources/BedrockService/Models/Amazon/Nova/Nova.swift diff --git a/Sources/Models/Amazon/Nova/NovaBedrockModels.swift b/Sources/BedrockService/Models/Amazon/Nova/NovaBedrockModels.swift similarity index 100% rename from Sources/Models/Amazon/Nova/NovaBedrockModels.swift rename to Sources/BedrockService/Models/Amazon/Nova/NovaBedrockModels.swift diff --git a/Sources/Models/Amazon/Nova/NovaImageResolutionValidator.swift b/Sources/BedrockService/Models/Amazon/Nova/NovaImageResolutionValidator.swift similarity index 100% rename from Sources/Models/Amazon/Nova/NovaImageResolutionValidator.swift rename to Sources/BedrockService/Models/Amazon/Nova/NovaImageResolutionValidator.swift diff --git a/Sources/Models/Amazon/Nova/NovaRequestBody.swift b/Sources/BedrockService/Models/Amazon/Nova/NovaRequestBody.swift similarity index 100% rename from Sources/Models/Amazon/Nova/NovaRequestBody.swift rename to Sources/BedrockService/Models/Amazon/Nova/NovaRequestBody.swift diff --git a/Sources/Models/Amazon/Nova/NovaResponseBody.swift b/Sources/BedrockService/Models/Amazon/Nova/NovaResponseBody.swift similarity index 100% rename from Sources/Models/Amazon/Nova/NovaResponseBody.swift rename to Sources/BedrockService/Models/Amazon/Nova/NovaResponseBody.swift diff --git a/Sources/Models/Amazon/TaskType.swift b/Sources/BedrockService/Models/Amazon/TaskType.swift similarity index 100% rename from Sources/Models/Amazon/TaskType.swift rename to Sources/BedrockService/Models/Amazon/TaskType.swift diff --git a/Sources/Models/Amazon/Titan/TitanImageResolutionValidator.swift b/Sources/BedrockService/Models/Amazon/Titan/TitanImageResolutionValidator.swift similarity index 100% rename from Sources/Models/Amazon/Titan/TitanImageResolutionValidator.swift rename to Sources/BedrockService/Models/Amazon/Titan/TitanImageResolutionValidator.swift diff --git a/Sources/Models/Amazon/Titan/TitanRequestBody.swift b/Sources/BedrockService/Models/Amazon/Titan/TitanRequestBody.swift similarity index 100% rename from Sources/Models/Amazon/Titan/TitanRequestBody.swift rename to Sources/BedrockService/Models/Amazon/Titan/TitanRequestBody.swift diff --git a/Sources/Models/Amazon/Titan/TitanResponseBody.swift b/Sources/BedrockService/Models/Amazon/Titan/TitanResponseBody.swift similarity index 100% rename from Sources/Models/Amazon/Titan/TitanResponseBody.swift rename to Sources/BedrockService/Models/Amazon/Titan/TitanResponseBody.swift diff --git a/Sources/Models/Amazon/Titan/TitanText.swift b/Sources/BedrockService/Models/Amazon/Titan/TitanText.swift similarity index 100% rename from Sources/Models/Amazon/Titan/TitanText.swift rename to Sources/BedrockService/Models/Amazon/Titan/TitanText.swift diff --git a/Sources/Models/Amazon/Titan/TitanTextBedrockModels.swift b/Sources/BedrockService/Models/Amazon/Titan/TitanTextBedrockModels.swift similarity index 100% rename from Sources/Models/Amazon/Titan/TitanTextBedrockModels.swift rename to Sources/BedrockService/Models/Amazon/Titan/TitanTextBedrockModels.swift diff --git a/Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddings.swift b/Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddings.swift similarity index 100% rename from Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddings.swift rename to Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddings.swift diff --git a/Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddingsBedrockModels.swift b/Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddingsBedrockModels.swift similarity index 100% rename from Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddingsBedrockModels.swift rename to Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddingsBedrockModels.swift diff --git a/Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddingsRequestBody.swift b/Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddingsRequestBody.swift similarity index 100% rename from Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddingsRequestBody.swift rename to Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddingsRequestBody.swift diff --git a/Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddingsResponseBody.swift b/Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddingsResponseBody.swift similarity index 100% rename from Sources/Models/Amazon/TitanEmbeddings/TitanEmbeddingsResponseBody.swift rename to Sources/BedrockService/Models/Amazon/TitanEmbeddings/TitanEmbeddingsResponseBody.swift diff --git a/Sources/Models/Anthropic/Anthropic.swift b/Sources/BedrockService/Models/Anthropic/Anthropic.swift similarity index 100% rename from Sources/Models/Anthropic/Anthropic.swift rename to Sources/BedrockService/Models/Anthropic/Anthropic.swift diff --git a/Sources/Models/Anthropic/AnthropicBedrockModels.swift b/Sources/BedrockService/Models/Anthropic/AnthropicBedrockModels.swift similarity index 100% rename from Sources/Models/Anthropic/AnthropicBedrockModels.swift rename to Sources/BedrockService/Models/Anthropic/AnthropicBedrockModels.swift diff --git a/Sources/Models/Anthropic/AnthropicRequestBody.swift b/Sources/BedrockService/Models/Anthropic/AnthropicRequestBody.swift similarity index 100% rename from Sources/Models/Anthropic/AnthropicRequestBody.swift rename to Sources/BedrockService/Models/Anthropic/AnthropicRequestBody.swift diff --git a/Sources/Models/Anthropic/AnthropicResponseBody.swift b/Sources/BedrockService/Models/Anthropic/AnthropicResponseBody.swift similarity index 100% rename from Sources/Models/Anthropic/AnthropicResponseBody.swift rename to Sources/BedrockService/Models/Anthropic/AnthropicResponseBody.swift diff --git a/Sources/Models/Cohere/CohereBedrockModels.swift b/Sources/BedrockService/Models/Cohere/CohereBedrockModels.swift similarity index 100% rename from Sources/Models/Cohere/CohereBedrockModels.swift rename to Sources/BedrockService/Models/Cohere/CohereBedrockModels.swift diff --git a/Sources/Models/DeepSeek/DeepSeek.swift b/Sources/BedrockService/Models/DeepSeek/DeepSeek.swift similarity index 100% rename from Sources/Models/DeepSeek/DeepSeek.swift rename to Sources/BedrockService/Models/DeepSeek/DeepSeek.swift diff --git a/Sources/Models/DeepSeek/DeepSeekBedrockModels.swift b/Sources/BedrockService/Models/DeepSeek/DeepSeekBedrockModels.swift similarity index 100% rename from Sources/Models/DeepSeek/DeepSeekBedrockModels.swift rename to Sources/BedrockService/Models/DeepSeek/DeepSeekBedrockModels.swift diff --git a/Sources/Models/DeepSeek/DeepSeekRequestBody.swift b/Sources/BedrockService/Models/DeepSeek/DeepSeekRequestBody.swift similarity index 100% rename from Sources/Models/DeepSeek/DeepSeekRequestBody.swift rename to Sources/BedrockService/Models/DeepSeek/DeepSeekRequestBody.swift diff --git a/Sources/Models/DeepSeek/DeepSeekResponseBody.swift b/Sources/BedrockService/Models/DeepSeek/DeepSeekResponseBody.swift similarity index 100% rename from Sources/Models/DeepSeek/DeepSeekResponseBody.swift rename to Sources/BedrockService/Models/DeepSeek/DeepSeekResponseBody.swift diff --git a/Sources/Models/Jamba/JambaBedrockModels.swift b/Sources/BedrockService/Models/Jamba/JambaBedrockModels.swift similarity index 100% rename from Sources/Models/Jamba/JambaBedrockModels.swift rename to Sources/BedrockService/Models/Jamba/JambaBedrockModels.swift diff --git a/Sources/Models/Llama/Llama.swift b/Sources/BedrockService/Models/Llama/Llama.swift similarity index 100% rename from Sources/Models/Llama/Llama.swift rename to Sources/BedrockService/Models/Llama/Llama.swift diff --git a/Sources/Models/Llama/LlamaBedrockModels.swift b/Sources/BedrockService/Models/Llama/LlamaBedrockModels.swift similarity index 100% rename from Sources/Models/Llama/LlamaBedrockModels.swift rename to Sources/BedrockService/Models/Llama/LlamaBedrockModels.swift diff --git a/Sources/Models/Llama/LlamaRequestBody.swift b/Sources/BedrockService/Models/Llama/LlamaRequestBody.swift similarity index 100% rename from Sources/Models/Llama/LlamaRequestBody.swift rename to Sources/BedrockService/Models/Llama/LlamaRequestBody.swift diff --git a/Sources/Models/Llama/LlamaResponseBody.swift b/Sources/BedrockService/Models/Llama/LlamaResponseBody.swift similarity index 100% rename from Sources/Models/Llama/LlamaResponseBody.swift rename to Sources/BedrockService/Models/Llama/LlamaResponseBody.swift diff --git a/Sources/Models/Mistral/MistralBedrockModels.swift b/Sources/BedrockService/Models/Mistral/MistralBedrockModels.swift similarity index 100% rename from Sources/Models/Mistral/MistralBedrockModels.swift rename to Sources/BedrockService/Models/Mistral/MistralBedrockModels.swift diff --git a/Sources/Models/OpenAI/OpenAI.swift b/Sources/BedrockService/Models/OpenAI/OpenAI.swift similarity index 100% rename from Sources/Models/OpenAI/OpenAI.swift rename to Sources/BedrockService/Models/OpenAI/OpenAI.swift diff --git a/Sources/Models/OpenAI/OpenAIBedrockModels.swift b/Sources/BedrockService/Models/OpenAI/OpenAIBedrockModels.swift similarity index 100% rename from Sources/Models/OpenAI/OpenAIBedrockModels.swift rename to Sources/BedrockService/Models/OpenAI/OpenAIBedrockModels.swift diff --git a/Sources/Models/OpenAI/OpenAIRequestBody.swift b/Sources/BedrockService/Models/OpenAI/OpenAIRequestBody.swift similarity index 100% rename from Sources/Models/OpenAI/OpenAIRequestBody.swift rename to Sources/BedrockService/Models/OpenAI/OpenAIRequestBody.swift diff --git a/Sources/Models/OpenAI/OpenAIResponseBody.swift b/Sources/BedrockService/Models/OpenAI/OpenAIResponseBody.swift similarity index 100% rename from Sources/Models/OpenAI/OpenAIResponseBody.swift rename to Sources/BedrockService/Models/OpenAI/OpenAIResponseBody.swift diff --git a/Sources/Parameters/ConverseParameters.swift b/Sources/BedrockService/Parameters/ConverseParameters.swift similarity index 100% rename from Sources/Parameters/ConverseParameters.swift rename to Sources/BedrockService/Parameters/ConverseParameters.swift diff --git a/Sources/Parameters/EmbeddingsParameters.swift b/Sources/BedrockService/Parameters/EmbeddingsParameters.swift similarity index 100% rename from Sources/Parameters/EmbeddingsParameters.swift rename to Sources/BedrockService/Parameters/EmbeddingsParameters.swift diff --git a/Sources/Parameters/ImageGenerationParameters.swift b/Sources/BedrockService/Parameters/ImageGenerationParameters.swift similarity index 100% rename from Sources/Parameters/ImageGenerationParameters.swift rename to Sources/BedrockService/Parameters/ImageGenerationParameters.swift diff --git a/Sources/Parameters/ParameterName.swift b/Sources/BedrockService/Parameters/ParameterName.swift similarity index 100% rename from Sources/Parameters/ParameterName.swift rename to Sources/BedrockService/Parameters/ParameterName.swift diff --git a/Sources/Parameters/Parameters.swift b/Sources/BedrockService/Parameters/Parameters.swift similarity index 100% rename from Sources/Parameters/Parameters.swift rename to Sources/BedrockService/Parameters/Parameters.swift diff --git a/Sources/Parameters/TextGenerationParameters.swift b/Sources/BedrockService/Parameters/TextGenerationParameters.swift similarity index 100% rename from Sources/Parameters/TextGenerationParameters.swift rename to Sources/BedrockService/Parameters/TextGenerationParameters.swift diff --git a/Sources/Protocols/BedrockClientProtocol.swift b/Sources/BedrockService/Protocols/BedrockClientProtocol.swift similarity index 100% rename from Sources/Protocols/BedrockClientProtocol.swift rename to Sources/BedrockService/Protocols/BedrockClientProtocol.swift diff --git a/Sources/Protocols/BedrockConfigProtocol.swift b/Sources/BedrockService/Protocols/BedrockConfigProtocol.swift similarity index 100% rename from Sources/Protocols/BedrockConfigProtocol.swift rename to Sources/BedrockService/Protocols/BedrockConfigProtocol.swift diff --git a/Sources/Protocols/BedrockRuntimeClientProtocol.swift b/Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift similarity index 100% rename from Sources/Protocols/BedrockRuntimeClientProtocol.swift rename to Sources/BedrockService/Protocols/BedrockRuntimeClientProtocol.swift diff --git a/Sources/Region.swift b/Sources/BedrockService/Region.swift similarity index 100% rename from Sources/Region.swift rename to Sources/BedrockService/Region.swift From e3626a959f27c4329d447d37c8fb945c92edc7c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 21 Oct 2025 11:24:26 +0200 Subject: [PATCH 2/5] add doc in the header --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2b31e3b2..bd428be9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ A Swift library for interacting with [Amazon Bedrock](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) foundation models. +📖 **[Complete Documentation](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation)** - Comprehensive guides and API reference + ## Acknowledgment This library and playground have been written by [Mona Dierickx](https://www.linkedin.com/in/mona-dierickx/), during her last year of studies at [HoGent](https://www.hogent.be/), Belgium. From 91611185d621e120c7bb4c5d57bd7119db5a6a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 21 Oct 2025 11:27:26 +0200 Subject: [PATCH 3/5] move acknowledgment section down the page --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index bd428be9..cb888bd9 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,6 @@ A Swift library for interacting with [Amazon Bedrock](https://docs.aws.amazon.co 📖 **[Complete Documentation](https://swiftpackageindex.com/build-on-aws/swift-bedrock-library/documentation)** - Comprehensive guides and API reference -## Acknowledgment - -This library and playground have been written by [Mona Dierickx](https://www.linkedin.com/in/mona-dierickx/), during her last year of studies at [HoGent](https://www.hogent.be/), Belgium. - -Thank you for your enthusiasm and positive attitude during the three months we worked together (February 2025 - May 2025). - -Thank you Professor Steven Van Impe for allowing us to work with these young talents. - ## TL;DR - Quick Start ```swift @@ -67,4 +59,12 @@ Explore the [Examples](./Examples/) directory for complete sample applications i - Streaming responses - Image generation - iOS math solver app -- Web playground with frontend/backend \ No newline at end of file +- Web playground with frontend/backend + +## Acknowledgment + +This library and playground have been written by [Mona Dierickx](https://www.linkedin.com/in/mona-dierickx/), during her last year of studies at [HoGent](https://www.hogent.be/), Belgium. + +Thank you for your enthusiasm and positive attitude during the three months we worked together (February 2025 - May 2025). + +Thank you Professor Steven Van Impe for allowing us to work with these young talents. From f57fd7575266bc63c486e41aef0a45af9992242a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 21 Oct 2025 11:40:15 +0200 Subject: [PATCH 4/5] fix ci --- .github/workflows/pull_request.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index a29c0224..3ec633b2 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -40,9 +40,17 @@ jobs: # libssl-dev which is not installed on swift:6.2-noble # Using swift:6.2-amazonlinux2 is not a solution # because the @checkout action doesn't works on ALI2 (requires Node.js 20) + # will re-enable when Amazon Linux 2023 will be available api_breakage_check_enabled: false api_breakage_check_container_image: "swift:6.2-noble" - docs_check_container_image: "swift:6.2-noble" + + # disabled because images needs libssl-dev (which excludes swift:6.2-noble) + # and Node 20+ (for the checkout action), whcih excludes Amazon Linux 2 + # will re-enable when Amazon Linux 2023 will be available + docs_check_enabled: false + docs_check_container_image: "swift:6.2-amazonlinux2" + + format_check_enabled: true format_check_container_image: "swift:6.2-noble" yamllint_check_enabled: true From cdec2b58255b828fa5087540e0130b868873aeba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 21 Oct 2025 11:50:08 +0200 Subject: [PATCH 5/5] add test for embeddings --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 3ec633b2..9e1978d6 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -91,7 +91,7 @@ jobs: # We pass the list of examples here, but we can't pass an array as argument # Instead, we pass a String with a valid JSON array. # The workaround is mentioned here https://github.com/orgs/community/discussions/11692 - examples: "[ 'api-key', 'converse', 'converse-stream', 'openai', 'text_chat' ]" + examples: "[ 'api-key', 'converse', 'converse-stream', 'embeddings', 'openai', 'text_chat' ]" swift-6-language-mode: name: Swift 6 Language Mode