├── LICENSE ├── OMFspec.yml ├── README.md ├── examples ├── chatbot │ ├── README.md │ ├── chatbot_backend │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── chatbot_frontend │ │ ├── main.py │ │ └── requirements.txt ├── clients │ └── example_python_client │ │ ├── .github │ │ └── workflows │ │ │ └── python.yml │ │ ├── .gitlab-ci.yml │ │ ├── .openapi-generator-ignore │ │ ├── .openapi-generator │ │ ├── FILES │ │ └── VERSION │ │ ├── .travis.yml │ │ ├── README.md │ │ ├── docs │ │ ├── ContentItem.md │ │ ├── DefaultApi.md │ │ ├── Message.md │ │ ├── MessageContent.md │ │ ├── Metadata.md │ │ └── ResponseMessage.md │ │ ├── git_push.sh │ │ ├── openapi_client │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ └── default_api.py │ │ ├── api_client.py │ │ ├── api_response.py │ │ ├── configuration.py │ │ ├── exceptions.py │ │ ├── models │ │ │ ├── __init__.py │ │ │ ├── content_item.py │ │ │ ├── message.py │ │ │ ├── message_content.py │ │ │ ├── metadata.py │ │ │ └── response_message.py │ │ ├── py.typed │ │ └── rest.py │ │ ├── pyproject.toml │ │ ├── requirements.txt │ │ ├── setup.cfg │ │ ├── setup.py │ │ ├── test-requirements.txt │ │ ├── test │ │ ├── __init__.py │ │ ├── test_content_item.py │ │ ├── test_default_api.py │ │ ├── test_message.py │ │ ├── test_message_content.py │ │ ├── test_metadata.py │ │ └── test_response_message.py │ │ └── tox.ini └── conversion_tools │ ├── README.md │ └── conversion-tools-py │ └── bedrock.py └── site_content ├── after.png └── before.png /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 open-llm-initiative 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OMFspec.yml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: Open Message Format 4 | description: | 5 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the "messages" object, which contains user and assistant interactions. The "messages" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 6 | version: 1.0.0 7 | paths: 8 | # endpoint for clients to send messages to 9 | /message: 10 | post: 11 | summary: This method allows developers to receive chat messages from an upstream client in a standard format. 12 | description: | 13 | This endpoint receives a message or messages object from a client and returns a message containing the content of the response. 14 | requestBody: 15 | required: true 16 | content: 17 | application/json: 18 | schema: 19 | $ref: "#/components/schemas/Messages" 20 | responses: 21 | "200": 22 | description: Response containing the content string. 23 | content: 24 | application/json: 25 | schema: 26 | $ref: "#/components/schemas/ResponseMessage" 27 | "400": 28 | description: Bad request 29 | "500": 30 | description: Internal server error 31 | 32 | components: 33 | schemas: 34 | # Array of messages 35 | Messages: 36 | type: array 37 | items: 38 | $ref: "#/components/schemas/Message" 39 | 40 | # Schema for a single message object 41 | Message: 42 | type: object 43 | properties: 44 | role: 45 | type: string 46 | description: | 47 | Role of the message sender. Examples include: 48 | - `system`: For system messages 49 | - `user`: For messages from the user 50 | - `assistant`: For messages from the assistant 51 | content: 52 | oneOf: 53 | - type: array 54 | items: 55 | $ref: "#/components/schemas/ContentItem" 56 | description: | 57 | Content of the message. It can be a list of content items, such as text and images. 58 | - type: string 59 | description: text content of the message, 60 | required: 61 | - role 62 | - content 63 | 64 | # Schema for a single content item within a message 65 | ContentItem: 66 | type: object 67 | properties: 68 | type: 69 | type: string 70 | description: The type of content, such as text or image URL. 71 | text: 72 | type: string 73 | description: The text content. Required if type is 'text'. 74 | image_url: 75 | type: object 76 | properties: 77 | url: 78 | type: string 79 | description: The URL of the image. 80 | description: The image URL object. Required if type is 'image_url'. 81 | source: 82 | type: object 83 | properties: 84 | type: 85 | type: string 86 | enum: [base64] 87 | description: The type of source, such as base64. 88 | media_type: 89 | type: string 90 | enum: [image/jpeg, image/png, image/gif, image/webp] 91 | description: The media type of the image. 92 | data: 93 | type: string 94 | format: base64 95 | description: The base64-encoded image data. 96 | description: The source object. Required if type is 'image'. 97 | 98 | required: 99 | - type 100 | oneOf: 101 | - required: ["text"] 102 | - required: ["image_url"] 103 | - required: ["source"] 104 | 105 | # Schema for the response message 106 | ResponseMessage: 107 | type: object 108 | properties: 109 | content: 110 | type: string 111 | description: The content of the response message. 112 | refusal: 113 | type: string 114 | nullable: true 115 | description: The refusal message if applicable, or null if not. 116 | role: 117 | type: string 118 | description: The role of the author of the message (e.g., 'assistant'). 119 | metadata: 120 | $ref: "#/components/schemas/Metadata" 121 | required: 122 | - content 123 | - role 124 | 125 | # Schema for the metadata object 126 | Metadata: 127 | type: object 128 | properties: 129 | model: 130 | type: string 131 | description: The name of the model used to generate the response. 132 | tokens: 133 | type: string 134 | description: The number of tokens used to generate the response. 135 | required: 136 | - model 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Message Format (OMF) 2 | ## Overview 3 | OMF is a compact, user-friendly specification that defines a lightweight API contract between client and server for building conversational agents, and defines a standard schema for the "messages" object, which contains user and assistant interactions. The "messages" object schema in OMF serves a dual purpose, to define a standard way to exchange user and assistant conversations from the client to your backend server, and from your backend server to an upstream LLM. 4 | 5 | Developers building conversational agents and generative AI applications waste precious time on two things: 6 | - Writing integration code that works only with specific client-side UI tools such as Gradio or Streamlit, which reduces their ability to experiment with new tools quickly. 7 | - Writing integration code what works with specific LLMs, reducing their ability to easily swap between LLMs for quick experimentation. 8 | 9 | Writing boilerplate integration code results in a slowed pace of development and a system that is rigid and difficult to change. OMF eliminates this undifferentiated heavy lifting by defining a standard API contract and a schema to exchange messages for conversational apps, and providers built-in convertors that make it easy for developers to experiment with different LLMs. 10 | 11 | The key advantages of using OMF are: 12 | - **Interoperability:** OMF standardizes prompt interactions between clients, servers, and LLMs. OMF is also interoperable across multiple popular LLMs. 13 | - **Extensibility:** OMF is designed to be extended in order to fit all developers' use cases. 14 | ## Benefits for Developers 15 | OMF simplifies the process of sending messages, making it easier to deploy conversational agents and other LLM-based tools and applications. It removes the guesswork for developers on how to send and receive messages. 16 | 17 | ![image](site_content/before.png) 18 | ![image (1)](site_content/after.png) 19 | 20 | ## Compatability 21 | | LLM Provider | Compatible with Stock OMF | Benefits from Additional Tooling | Requires Additional Tooling | 22 | | -------------- | ------------------------- | -------------------------------- | --------------------------- | 23 | | OpenAI | Yes | Yes | No | 24 | | Mistral AI | Yes | Yes | No | 25 | | Anthropic | Yes | Yes | No | 26 | | IBM | No | No | Yes | 27 | | Google | Yes (Requires Conversion) | Yes | No | 28 | | Amazon Bedrock | Yes (Requires Conversion) | Yes | No | 29 | | Cohere | Yes (Requires Conversion) | Yes | No | 30 | 31 | *Note: Some models have unique parameters such as `message_id` or `name`. These parameters, while easy to add for specific models, are not universal and therefore not included in the base specification. Some models also have certain function calling capabilities but due to function calls being more relevant to a full `ChatCompletions` setup, this is more relevant to the Open Completions API* 32 | 33 | ## How to Use OMF 34 | 35 | ### 1. Setting Up Your Endpoint 36 | 37 | #### a. Choose Your Development Environment 38 | 39 | You can use any programming language and web framework that supports OpenAPI specifications. Common choices include: 40 | 41 | - **Python**: Flask, FastAPI, or Django. 42 | - **JavaScript/Node.js**: Express.js. 43 | - **Java**: Spring Boot. 44 | - **Go**: Gin or Echo. 45 | - **Rust**: Actix-web or Rocket. 46 | 47 | #### b. Implement the API Endpoints 48 | 49 | If tools like `openapi-generator-cli` are not be viable for creating server stubs, you can manually implement the endpoint described in the OMF spec: 50 | 51 | 1. **Manually Create the `/message` Endpoint**: 52 | - In your chosen framework, define a POST endpoint `/message`. 53 | - Ensure that this endpoint accepts and processes the JSON payload as defined in the OMF spec. 54 | - The endpoint should accept a an array of the [`Message`](https://github.com/open-llm-initiative/open-message-format/blob/main/OMFspec.yml#L41) object. Each message will have a `role` and a `content` field, and the content could be text, base64-encoded images, or image URLs. 55 | 56 | 2. **Message Handling Logic**: 57 | - Parse the incoming JSON request into appropriate data models. The [`Message`](https://github.com/open-llm-initiative/open-message-format/blob/main/OMFspec.yml#L41) object should be parsed with a `role` and an array of [`ContentItem`](https://github.com/open-llm-initiative/open-message-format/blob/main/OMFspec.yml#L65) objects. 58 | - Implement logic to handle different types of content, such as text, images, and image URLs. 59 | - If you want to, you can directly send the array to an LLM by just passing it in the messages parameter for many LLM providers. You may need to create some tools to convert depending on the model you use. 60 | 61 | 3. **Construct Responses**: 62 | - Based on the request, generate a response that follows the [`ResponseMessage`](https://github.com/open-llm-initiative/open-message-format/blob/main/OMFspec.yml#L106) schema outlined in the specification. 63 | 64 | #### c. Example Setup 65 | 66 | Here’s a simplified example of how to implement the `/message` endpoint in Python using Flask: 67 | 68 | ```python 69 | from flask import Flask, request, jsonify 70 | import openai 71 | 72 | app = Flask(__name__) 73 | 74 | @app.route('/message', methods=['POST']) 75 | def handle_message(): 76 | messages = request.json 77 | 78 | try: 79 | # Send the received messages directly to OpenAI API using the correct method 80 | response = openai.chat.completions.create( 81 | model="gpt-4o-mini", messages=messages 82 | ) 83 | 84 | # Return the first choice's message directly 85 | return response.choices[0].message.content, 200 86 | except openai.error.OpenAIError as e: 87 | # Handle OpenAI API errors 88 | return {"error": str(e)}, 500 89 | 90 | return jsonify(response_message) 91 | 92 | if __name__ == '__main__': 93 | app.run(port=8080) 94 | ``` 95 | 96 | #### d. Testing Locally 97 | 98 | Once the endpoint is implemented, you can test it locally using `curl`, Postman, or any other HTTP client. For example, you can send the following request with curl: 99 | 100 | ```bash 101 | curl -X POST http://localhost:8080/message \ 102 | -H "Content-Type: application/json" \ 103 | -d '[ 104 | { 105 | "role": "user", 106 | "content": "Hello World" 107 | } 108 | ]' 109 | ``` 110 | 111 | ### 3. Testing Your Implementation 112 | 113 | After setting up the server, you can test the `/message` endpoint using tools like: 114 | 115 | - **curl** (as shown above) 116 | - **Postman**: Import the OpenAPI specification and generate requests directly to interact with your locally running server. 117 | 118 | Ensure that your endpoint processes the incoming messages correctly and returns appropriate responses in line with the OMF specification. 119 | 120 | ### 4. Deploying Your API 121 | 122 | Once your API is working locally, you can deploy it using your preferred method, such as: 123 | 124 | - **Containerization**: Use Docker to containerize your application and deploy it to cloud services like AWS, Azure, or GCP. 125 | - **Dedicated Server**: Run the application on a dedicated server using a production-ready web server and reverse proxy. 126 | 127 | ### Extending the Specification 128 | OMF is designed to be flexible and generic, allowing users to extend and expand the specification to fit specific needs. For instance, users can add arguments specific to OpenAI roles or modify the specification for providers like Cohere, who require separate `message` and `chat_history` parameters. An example modification might include adding the `name` parameter for the OpenAI `user` role. 129 | 130 | ``` yaml 131 | Message: 132 | type: object 133 | properties: 134 | role: 135 | type: string 136 | description: | 137 | Role of the message sender. Examples include: 138 | - `system`: For system messages 139 | - `user`: For messages from the user 140 | - `assistant`: For messages from the assistant 141 | content: 142 | oneOf: 143 | - type: array 144 | items: 145 | $ref: "#/components/schemas/ContentItem" 146 | description: | 147 | Content of the message. It can be a list of content items, such as text and images. 148 | - type: string 149 | description: text content of the message, 150 | name: 151 | type: string 152 | description: the name of the user sending this message 153 | required: 154 | - role 155 | - content 156 | ``` 157 | *This is a modification to the base spec that adjusts for the name parameter in the OpenAI `user` message, allowing developers to have chats with multiple people* 158 | ```yaml 159 | name: 160 | type: string 161 | description: | 162 | The name of the user sending the message. 163 | ``` 164 | *This is the section that was added to the spec in the above* 165 | 166 | ## Roadmap 167 | Future improvements include: 168 | 169 | | Feature or Improvement | Description | 170 | | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | 171 | | Additional message metadata parameter | Allows servers to gain more insight into the origins or details of the messages object before it is sent to an LLM. | 172 | | Extended version of the spec for Open Completions API | Currently being worked on to provide more robust functionality. | 173 | | `dependentRequired` keyword for OpenAPI 3.1.0 | Will be implemented once more tools support OpenAPI 3.1.0, ensuring content types align with the `type` in `ContentItem`. | 174 | | Add more details to the `Metadata` object. | Possible additions include a `timestamp` parameter, a `completion_tokens` parameter, and a `prompt_tokens` parameter | 175 | 176 | We encourage you to suggest and upvote new possible roadmap items via github issues. 177 | -------------------------------------------------------------------------------- /examples/chatbot/README.md: -------------------------------------------------------------------------------- 1 | # Project Name 2 | 3 | This project is a chatbot application with a Python frontend and a Rust backend. The Python frontend handles user interactions and sends requests to the Rust backend, which processes the messages. 4 | 5 | ## Project Structure 6 | 7 | - `main.py`: This is the Python frontend, which interacts with the user and communicates with the Rust backend via HTTP requests. 8 | - `main.rs`: This is the Rust backend that processes the messages received from the Python frontend and returns appropriate responses. 9 | 10 | ## Prerequisites 11 | 12 | To run this project, you'll need the following: 13 | 14 | - **Python 3.x**: Ensure Python is installed on your system. 15 | - **Rust**: Ensure Rust installed on your system. 16 | 17 | ## Installation 18 | 19 | ### 1. Clone the Repository 20 | 21 | ```bash 22 | git clone https://github.com/open-llm-initiative/open-message-format.git 23 | cd .\examples\chatbot\ 24 | ``` 25 | 26 | ### Step 2: Set Up a Python Virtual Environment and Install Dependencies 27 | 28 | It is recommended to use a virtual environment to manage your Python dependencies. Follow these steps to set up a virtual environment: 29 | 30 | 1. Navigate to the directory containing `main.py`: 31 | 32 | ```bash 33 | cd .\chatbot_frontend\ 34 | ``` 35 |   36 | 2. Create a virtual environment: 37 | 38 | ```bash 39 | python -m venv .venv 40 | ``` 41 | 42 | This will create a directory named `.venv` containing the virtual environment. 43 |   44 | 3. Activate the virtual environment: 45 | 46 | - On **Windows**: 47 | 48 | ```bash 49 | .venv\Scripts\activate 50 | ``` 51 | 52 | - On **macOS/Linux**: 53 | 54 | ```bash 55 | source .venv/bin/activate 56 | ``` 57 |   58 | 4. Install the required Python dependencies: 59 | 60 | ```bash 61 | pip install -r requirements.txt 62 | ``` 63 |   64 | 5. Go back to the directory containing both the front and backend: 65 | 66 | ```bash 67 | cd .. 68 | ``` 69 |   70 | ### Step 3. 71 | 72 | Create a .env file in the backend directory: 73 | 74 | 1. Navigate to the backend directory: 75 | 76 | ``` bash 77 | cd .\chatbot_backend\ 78 | ``` 79 |   80 | 2. create a new file called .env 81 |   82 | 3. In the .env file, set the values for `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, and `MISTRAL_API_KEY` 83 |   84 | 4. return to the chatbot directory: 85 | 86 | ```bash 87 | cd .. 88 | ``` 89 | 90 | ## Running the Project 91 | 92 | ### Step 1. Start the Rust Backend 93 | 94 | First, navigate to the directory containing main.rs and compile the Rust backend: 95 | 96 | ```bash 97 | cd .\chatbot_backend\ 98 | cargo run --release 99 | ``` 100 | 101 | ### Step 2. Run the Python Frontend 102 | 103 | In a terminal, Navigate to the directory containing main.py and run the Python script: 104 | 105 | ```bash 106 | cd .. 107 | cd .\chatbot_frontend\ 108 | python main.py 109 | ``` 110 | 111 | ### Step 3: Interact with the Chatbot 112 | 113 | Once both the backend and frontend are running, you can start interacting with the chatbot via the terminal in the front end. 114 | 115 | - Type your messages, and the chatbot will respond. 116 | - To exit the chatbot, type `exit` or `quit`. 117 | 118 | If you wish to change the LLM being used, you can interact with the backend via it's terminal, just type in one of three responses offered by the program: 119 | 120 | ```bash 121 | anthropic 122 | openai 123 | mistral 124 | ``` 125 | -------------------------------------------------------------------------------- /examples/chatbot/chatbot_backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chatbot_backend" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | actix-web = "4" 8 | serde = { version = "1.0.209", features = ["derive"] } 9 | serde_json = "1.0.127" 10 | reqwest = { version = "0.12.7", features = ["json"] } 11 | dotenv = "0.15" -------------------------------------------------------------------------------- /examples/chatbot/chatbot_backend/src/main.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{ 2 | post, 3 | web::{self}, 4 | App, HttpResponse, HttpServer, Responder, 5 | }; 6 | use dotenv::dotenv; 7 | use reqwest::{ 8 | header::{HeaderMap, HeaderValue, CONTENT_TYPE}, 9 | Client, 10 | }; 11 | use serde::{Deserialize, Serialize}; 12 | use std::{ 13 | env, 14 | io::{self, Write}, 15 | sync::{Arc, RwLock}, 16 | thread, 17 | }; 18 | 19 | #[derive(Deserialize, Serialize, Debug)] 20 | struct ImageSource { 21 | #[serde(rename = "type")] 22 | source_type: String, // e.g., "base64" 23 | media_type: String, // e.g., "image/jpeg" 24 | data: String, // Base64-encoded image data 25 | } 26 | 27 | #[derive(Deserialize, Serialize, Debug)] 28 | struct ImageUrl { 29 | url: String, 30 | } 31 | 32 | #[derive(Deserialize, Serialize, Debug)] 33 | #[serde(tag = "type", rename_all = "snake_case")] 34 | enum ContentItem { 35 | Text { text: String }, 36 | Image { source: ImageSource }, 37 | ImageUrl { image_url: ImageUrl }, 38 | } 39 | 40 | #[derive(Deserialize, Serialize, Debug)] 41 | #[serde(untagged)] 42 | enum MessageContent { 43 | StringContent(String), 44 | ObjectContent(Vec), 45 | } 46 | 47 | #[derive(Deserialize, Serialize, Debug)] 48 | struct Message { 49 | role: String, 50 | content: MessageContent, 51 | } 52 | #[derive(Serialize, Deserialize)] 53 | struct MessageResponse { 54 | content: String, 55 | refusal: Option, 56 | role: String, 57 | } 58 | 59 | enum Provider { 60 | OpenAI, 61 | Anthropic, 62 | Mistral, 63 | } 64 | 65 | #[post("/message")] 66 | async fn message_endpoint( 67 | state: web::Data, 68 | item: web::Json>, 69 | ) -> impl Responder { 70 | let provider = state.provider.read().unwrap(); 71 | 72 | let response = match *provider { 73 | Provider::OpenAI => prompt_openai(&item).await, 74 | Provider::Anthropic => prompt_anthropic(&item).await, 75 | Provider::Mistral => prompt_mistral(&item).await, 76 | }; 77 | match response { 78 | Ok(content) => HttpResponse::Ok().json(content), 79 | Err(e) => HttpResponse::BadRequest().body(e.to_string()), 80 | } 81 | } 82 | 83 | struct AppState { 84 | provider: Arc>, 85 | } 86 | 87 | fn start_cli(provider: Arc>) { 88 | thread::spawn(move || loop { 89 | print!("Enter provider (openai/anthropic/mistral): "); 90 | io::stdout().flush().unwrap(); 91 | 92 | let mut input = String::new(); 93 | io::stdin().read_line(&mut input).unwrap(); 94 | 95 | let trimmed_input = input.trim(); 96 | let mut provider_write = provider.write().unwrap(); 97 | 98 | match trimmed_input { 99 | "openai" => { 100 | *provider_write = Provider::OpenAI; 101 | println!("Switched to OpenAI"); 102 | } 103 | "anthropic" => { 104 | *provider_write = Provider::Anthropic; 105 | println!("Switched to Anthropic"); 106 | } 107 | "mistral" => { 108 | *provider_write = Provider::Mistral; 109 | println!("Switched to Mistral"); 110 | } 111 | _ => println!("Invalid provider. Please enter 'openai' or 'anthropic'."), 112 | } 113 | }); 114 | } 115 | 116 | #[allow(dead_code)] 117 | async fn prompt_openai( 118 | messages: &[Message], 119 | ) -> Result> { 120 | // Load API key from environment 121 | let api_key = env::var("OPENAI_API_KEY")?; 122 | 123 | // Create an HTTP client 124 | let client = Client::new(); 125 | 126 | // Define the request body for OpenAI 127 | let body = serde_json::json!({ 128 | "model": "gpt-4o-mini", // Replace with your desired model 129 | "messages": messages 130 | }); 131 | 132 | // Send the request to OpenAI 133 | let response = client 134 | .post("https://api.openai.com/v1/chat/completions") 135 | .bearer_auth(api_key) 136 | .json(&body) 137 | .send() 138 | .await?; 139 | 140 | // Parse the response directly into MessageResponse 141 | let response_json: serde_json::Value = response.json().await?; 142 | let response_message: MessageResponse = serde_json::from_value( 143 | response_json 144 | .get("choices") 145 | .and_then(|choices| choices.get(0)) 146 | .and_then(|choice| choice.get("message").cloned()) 147 | .ok_or("Failed to parse OpenAI response")?, 148 | )?; 149 | 150 | Ok(response_message) 151 | } 152 | 153 | #[allow(dead_code)] 154 | async fn prompt_anthropic( 155 | messages: &[Message], 156 | ) -> Result> { 157 | // Load API key from environment 158 | let api_key = env::var("ANTHROPIC_API_KEY")?; 159 | 160 | // Set up the headers 161 | let mut headers = HeaderMap::new(); 162 | headers.insert("x-api-key", HeaderValue::from_str(&api_key)?); 163 | headers.insert("anthropic-version", HeaderValue::from_static("2023-06-01")); 164 | headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); 165 | 166 | // Create an HTTP client 167 | let client = Client::new(); 168 | 169 | // Define the request body for Anthropic 170 | let body = serde_json::json!({ 171 | "model": "claude-3-haiku-20240307", // Replace with the appropriate model from Anthropic 172 | "messages": messages, // Pass the messages directly 173 | "max_tokens": 1024 174 | }); 175 | 176 | // Send the request to Anthropic 177 | let response = client 178 | .post("https://api.anthropic.com/v1/messages") // Correct endpoint for Anthropic API 179 | .headers(headers) 180 | .json(&body) 181 | .send() 182 | .await?; 183 | 184 | // Parse the response and extract the "content" field 185 | let response_json: serde_json::Value = response.json().await?; 186 | let response_text = 187 | response_json 188 | .get("content") 189 | .and_then(|content| content.get(0)) 190 | .and_then(|texts| texts.get("text")) 191 | .and_then(|text| text.as_str()) 192 | .ok_or("Failed to parse Anthropic response")?; 193 | 194 | // Construct the MessageResponse object 195 | let response_message = MessageResponse { 196 | content: response_text.to_string(), 197 | refusal: None, 198 | role: "assistant".to_string(), 199 | }; 200 | 201 | Ok(response_message) 202 | } 203 | 204 | #[allow(dead_code)] 205 | async fn prompt_mistral( 206 | messages: &[Message], 207 | ) -> Result> { 208 | // Load API key from environment 209 | let api_key = env::var("MISTRAL_API_KEY")?; 210 | 211 | let mut headers = HeaderMap::new(); 212 | headers.insert("Accept", HeaderValue::from_static("application/json")); 213 | headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); 214 | 215 | // Create an HTTP client 216 | let client = Client::new(); 217 | 218 | let body = serde_json::json!({ 219 | "model": "open-mistral-nemo", // Replace with your desired model 220 | "messages": messages 221 | }); 222 | 223 | let response = client 224 | .post("https://api.mistral.ai/v1/chat/completions") 225 | .headers(headers) 226 | .bearer_auth(api_key) 227 | .json(&body) 228 | .send() 229 | .await?; 230 | 231 | let response_json: serde_json::Value = response.json().await?; 232 | 233 | let response_text = response_json 234 | .get("choices") 235 | .and_then(|choices| choices.get(0)) 236 | .and_then(|choice| choice.get("message")) 237 | .and_then(|content| content.get("content")) 238 | .and_then(|text| text.as_str()) 239 | .ok_or("Failed to parse Mistral response")?; 240 | 241 | let response_message = MessageResponse { 242 | content: response_text.to_string(), 243 | refusal: None, 244 | role: "assistant".to_string(), 245 | }; 246 | 247 | Ok(response_message) 248 | } 249 | 250 | #[actix_web::main] 251 | async fn main() -> std::io::Result<()> { 252 | dotenv().ok(); 253 | 254 | let provider = Arc::new(RwLock::new(Provider::OpenAI)); 255 | 256 | let state = web::Data::new(AppState { 257 | provider: provider.clone(), 258 | }); 259 | 260 | // Start the CLI in a separate thread 261 | start_cli(provider); 262 | 263 | HttpServer::new(move || App::new().app_data(state.clone()).service(message_endpoint)) 264 | .bind("127.0.0.1:8080")? 265 | .run() 266 | .await 267 | } 268 | -------------------------------------------------------------------------------- /examples/chatbot/chatbot_frontend/main.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from typing import List, Union, Optional 3 | 4 | 5 | class OMFClient: 6 | def __init__(self, base_url: str): 7 | self.base_url = base_url 8 | 9 | def send_messages(self, messages: List[dict], endpoint: str = "/message") -> dict: 10 | """Send multiple messages to the server.""" 11 | response = requests.post(f"{self.base_url}{endpoint}", json=messages) 12 | response.raise_for_status() 13 | return response.json() 14 | 15 | def create_text_content(self, text: str) -> dict: 16 | """Create a text content item.""" 17 | return {"type": "text", "text": text} 18 | 19 | def create_image_content( 20 | self, base64_data: str, media_type: str = "image/jpeg" 21 | ) -> dict: 22 | """Create an image content item.""" 23 | return { 24 | "type": "image", 25 | "source": {"type": "base64", "media_type": media_type, "data": base64_data}, 26 | } 27 | 28 | def create_image_url_content(self, url: str) -> dict: 29 | """Create an image URL content item.""" 30 | return {"type": "image_url", "image_url": {"url": url}} 31 | 32 | 33 | def run_chatbot(): 34 | client = OMFClient(base_url="http://localhost:8080") 35 | conversation = [] 36 | 37 | while True: 38 | # Get user input 39 | user_input = input("You: ") 40 | 41 | # Exit the loop if the user types 'exit' or 'quit' 42 | if user_input.lower() in ["exit", "quit"]: 43 | break 44 | 45 | # Create the user message and add it to the conversation history 46 | user_message = { 47 | "role": "user", 48 | "content": [client.create_text_content(user_input)], 49 | } 50 | conversation.append(user_message) 51 | 52 | # Send the entire conversation history to the backend 53 | response = client.send_messages(conversation) 54 | 55 | # Assume the last message in the response is the bot's reply 56 | bot_response = {"role": response["role"], "content": response["content"]} 57 | conversation.append( 58 | bot_response 59 | ) # Add the bot's response to the conversation history 60 | 61 | # Print the bot's response 62 | print(f"Bot: {bot_response['content']}") 63 | 64 | 65 | if __name__ == "__main__": 66 | run_chatbot() 67 | -------------------------------------------------------------------------------- /examples/chatbot/chatbot_frontend/requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /examples/clients/example_python_client/.github/workflows/python.yml: -------------------------------------------------------------------------------- 1 | # NOTE: This file is auto generated by OpenAPI Generator. 2 | # URL: https://openapi-generator.tech 3 | # 4 | # ref: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 5 | 6 | name: openapi_client Python package 7 | 8 | on: [push, pull_request] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] 17 | 18 | steps: 19 | - uses: actions/checkout@v3 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install flake8 pytest 28 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 29 | if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi 30 | - name: Lint with flake8 31 | run: | 32 | # stop the build if there are Python syntax errors or undefined names 33 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 34 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 35 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 36 | - name: Test with pytest 37 | run: | 38 | pytest 39 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # NOTE: This file is auto generated by OpenAPI Generator. 2 | # URL: https://openapi-generator.tech 3 | # 4 | # ref: https://docs.gitlab.com/ee/ci/README.html 5 | # ref: https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Python.gitlab-ci.yml 6 | 7 | stages: 8 | - test 9 | 10 | .pytest: 11 | stage: test 12 | script: 13 | - pip install -r requirements.txt 14 | - pip install -r test-requirements.txt 15 | - pytest --cov=openapi_client 16 | 17 | pytest-3.7: 18 | extends: .pytest 19 | image: python:3.7-alpine 20 | pytest-3.8: 21 | extends: .pytest 22 | image: python:3.8-alpine 23 | pytest-3.9: 24 | extends: .pytest 25 | image: python:3.9-alpine 26 | pytest-3.10: 27 | extends: .pytest 28 | image: python:3.10-alpine 29 | pytest-3.11: 30 | extends: .pytest 31 | image: python:3.11-alpine 32 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/.openapi-generator-ignore: -------------------------------------------------------------------------------- 1 | # OpenAPI Generator Ignore 2 | # Generated by openapi-generator https://github.com/openapitools/openapi-generator 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/.openapi-generator/FILES: -------------------------------------------------------------------------------- 1 | .github/workflows/python.yml 2 | .gitignore 3 | .gitlab-ci.yml 4 | .openapi-generator-ignore 5 | .travis.yml 6 | README.md 7 | docs/ContentItem.md 8 | docs/DefaultApi.md 9 | docs/Message.md 10 | docs/MessageContent.md 11 | docs/Metadata.md 12 | docs/ResponseMessage.md 13 | git_push.sh 14 | openapi_client/__init__.py 15 | openapi_client/api/__init__.py 16 | openapi_client/api/default_api.py 17 | openapi_client/api_client.py 18 | openapi_client/api_response.py 19 | openapi_client/configuration.py 20 | openapi_client/exceptions.py 21 | openapi_client/models/__init__.py 22 | openapi_client/models/content_item.py 23 | openapi_client/models/message.py 24 | openapi_client/models/message_content.py 25 | openapi_client/models/metadata.py 26 | openapi_client/models/response_message.py 27 | openapi_client/py.typed 28 | openapi_client/rest.py 29 | pyproject.toml 30 | requirements.txt 31 | setup.cfg 32 | setup.py 33 | test-requirements.txt 34 | test/__init__.py 35 | test/test_content_item.py 36 | test/test_default_api.py 37 | test/test_message.py 38 | test/test_message_content.py 39 | test/test_metadata.py 40 | test/test_response_message.py 41 | tox.ini 42 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/.openapi-generator/VERSION: -------------------------------------------------------------------------------- 1 | 7.8.0 2 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/.travis.yml: -------------------------------------------------------------------------------- 1 | # ref: https://docs.travis-ci.com/user/languages/python 2 | language: python 3 | python: 4 | - "3.7" 5 | - "3.8" 6 | - "3.9" 7 | - "3.10" 8 | - "3.11" 9 | # uncomment the following if needed 10 | #- "3.11-dev" # 3.11 development branch 11 | #- "nightly" # nightly build 12 | # command to install dependencies 13 | install: 14 | - "pip install -r requirements.txt" 15 | - "pip install -r test-requirements.txt" 16 | # command to run tests 17 | script: pytest --cov=openapi_client 18 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/README.md: -------------------------------------------------------------------------------- 1 | # openapi-client 2 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 3 | 4 | 5 | This Python package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project: 6 | 7 | - API version: 1.0.0 8 | - Package version: 1.0.0 9 | - Generator version: 7.8.0 10 | - Build package: org.openapitools.codegen.languages.PythonClientCodegen 11 | 12 | ## Requirements. 13 | 14 | Python 3.7+ 15 | 16 | ## Installation & Usage 17 | ### pip install 18 | 19 | If the python package is hosted on a repository, you can install directly using: 20 | 21 | ```sh 22 | pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git 23 | ``` 24 | (you may need to run `pip` with root permission: `sudo pip install git+https://github.com/GIT_USER_ID/GIT_REPO_ID.git`) 25 | 26 | Then import the package: 27 | ```python 28 | import openapi_client 29 | ``` 30 | 31 | ### Setuptools 32 | 33 | Install via [Setuptools](http://pypi.python.org/pypi/setuptools). 34 | 35 | ```sh 36 | python setup.py install --user 37 | ``` 38 | (or `sudo python setup.py install` to install the package for all users) 39 | 40 | Then import the package: 41 | ```python 42 | import openapi_client 43 | ``` 44 | 45 | ### Tests 46 | 47 | Execute `pytest` to run the tests. 48 | 49 | ## Getting Started 50 | 51 | Please follow the [installation procedure](#installation--usage) and then run the following: 52 | 53 | ```python 54 | 55 | import openapi_client 56 | from openapi_client.rest import ApiException 57 | from pprint import pprint 58 | 59 | # Defining the host is optional and defaults to http://localhost 60 | # See configuration.py for a list of all supported configuration parameters. 61 | configuration = openapi_client.Configuration( 62 | host = "http://localhost" 63 | ) 64 | 65 | 66 | 67 | # Enter a context with an instance of the API client 68 | with openapi_client.ApiClient(configuration) as api_client: 69 | # Create an instance of the API class 70 | api_instance = openapi_client.DefaultApi(api_client) 71 | message = [openapi_client.Message()] # List[Message] | 72 | 73 | try: 74 | # This method allows developers to receive chat messages from an upstream client in a standard format. 75 | api_response = api_instance.message_post(message) 76 | print("The response of DefaultApi->message_post:\n") 77 | pprint(api_response) 78 | except ApiException as e: 79 | print("Exception when calling DefaultApi->message_post: %s\n" % e) 80 | 81 | ``` 82 | 83 | ## Documentation for API Endpoints 84 | 85 | All URIs are relative to *http://localhost* 86 | 87 | Class | Method | HTTP request | Description 88 | ------------ | ------------- | ------------- | ------------- 89 | *DefaultApi* | [**message_post**](docs/DefaultApi.md#message_post) | **POST** /message | This method allows developers to receive chat messages from an upstream client in a standard format. 90 | 91 | 92 | ## Documentation For Models 93 | 94 | - [ContentItem](docs/ContentItem.md) 95 | - [Message](docs/Message.md) 96 | - [MessageContent](docs/MessageContent.md) 97 | - [Metadata](docs/Metadata.md) 98 | - [ResponseMessage](docs/ResponseMessage.md) 99 | 100 | 101 | 102 | ## Documentation For Authorization 103 | 104 | Endpoints do not require authorization. 105 | 106 | 107 | ## Author 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/docs/ContentItem.md: -------------------------------------------------------------------------------- 1 | # ContentItem 2 | 3 | 4 | ## Properties 5 | 6 | Name | Type | Description | Notes 7 | ------------ | ------------- | ------------- | ------------- 8 | **type** | **str** | The type of content, such as text or image URL. | 9 | **text** | **str** | The text content. Required if type is 'text'. | [optional] 10 | **image_url** | **object** | The image URL object. Required if type is 'image_url'. | [optional] 11 | **source** | **object** | The source object. Required if type is 'image'. | [optional] 12 | 13 | ## Example 14 | 15 | ```python 16 | from openapi_client.models.content_item import ContentItem 17 | 18 | # TODO update the JSON string below 19 | json = "{}" 20 | # create an instance of ContentItem from a JSON string 21 | content_item_instance = ContentItem.from_json(json) 22 | # print the JSON string representation of the object 23 | print(ContentItem.to_json()) 24 | 25 | # convert the object into a dict 26 | content_item_dict = content_item_instance.to_dict() 27 | # create an instance of ContentItem from a dict 28 | content_item_from_dict = ContentItem.from_dict(content_item_dict) 29 | ``` 30 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/docs/DefaultApi.md: -------------------------------------------------------------------------------- 1 | # openapi_client.DefaultApi 2 | 3 | All URIs are relative to *http://localhost* 4 | 5 | Method | HTTP request | Description 6 | ------------- | ------------- | ------------- 7 | [**message_post**](DefaultApi.md#message_post) | **POST** /message | This method allows developers to receive chat messages from an upstream client in a standard format. 8 | 9 | 10 | # **message_post** 11 | > ResponseMessage message_post(message) 12 | 13 | This method allows developers to receive chat messages from an upstream client in a standard format. 14 | 15 | This endpoint receives a message or messages object from a client and returns a message containing the content of the response. 16 | 17 | ### Example 18 | 19 | 20 | ```python 21 | import openapi_client 22 | from openapi_client.models.message import Message 23 | from openapi_client.models.response_message import ResponseMessage 24 | from openapi_client.rest import ApiException 25 | from pprint import pprint 26 | 27 | # Defining the host is optional and defaults to http://localhost 28 | # See configuration.py for a list of all supported configuration parameters. 29 | configuration = openapi_client.Configuration( 30 | host = "http://localhost" 31 | ) 32 | 33 | 34 | # Enter a context with an instance of the API client 35 | with openapi_client.ApiClient(configuration) as api_client: 36 | # Create an instance of the API class 37 | api_instance = openapi_client.DefaultApi(api_client) 38 | message = [openapi_client.Message()] # List[Message] | 39 | 40 | try: 41 | # This method allows developers to receive chat messages from an upstream client in a standard format. 42 | api_response = api_instance.message_post(message) 43 | print("The response of DefaultApi->message_post:\n") 44 | pprint(api_response) 45 | except Exception as e: 46 | print("Exception when calling DefaultApi->message_post: %s\n" % e) 47 | ``` 48 | 49 | 50 | 51 | ### Parameters 52 | 53 | 54 | Name | Type | Description | Notes 55 | ------------- | ------------- | ------------- | ------------- 56 | **message** | [**List[Message]**](Message.md)| | 57 | 58 | ### Return type 59 | 60 | [**ResponseMessage**](ResponseMessage.md) 61 | 62 | ### Authorization 63 | 64 | No authorization required 65 | 66 | ### HTTP request headers 67 | 68 | - **Content-Type**: application/json 69 | - **Accept**: application/json 70 | 71 | ### HTTP response details 72 | 73 | | Status code | Description | Response headers | 74 | |-------------|-------------|------------------| 75 | **200** | Response containing the content string. | - | 76 | **400** | Bad request | - | 77 | **500** | Internal server error | - | 78 | 79 | [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md) 80 | 81 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/docs/Message.md: -------------------------------------------------------------------------------- 1 | # Message 2 | 3 | 4 | ## Properties 5 | 6 | Name | Type | Description | Notes 7 | ------------ | ------------- | ------------- | ------------- 8 | **role** | **str** | Role of the message sender. Examples include: - `system`: For system messages - `user`: For messages from the user - `assistant`: For messages from the assistant | 9 | **content** | [**MessageContent**](MessageContent.md) | | 10 | 11 | ## Example 12 | 13 | ```python 14 | from openapi_client.models.message import Message 15 | 16 | # TODO update the JSON string below 17 | json = "{}" 18 | # create an instance of Message from a JSON string 19 | message_instance = Message.from_json(json) 20 | # print the JSON string representation of the object 21 | print(Message.to_json()) 22 | 23 | # convert the object into a dict 24 | message_dict = message_instance.to_dict() 25 | # create an instance of Message from a dict 26 | message_from_dict = Message.from_dict(message_dict) 27 | ``` 28 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/docs/MessageContent.md: -------------------------------------------------------------------------------- 1 | # MessageContent 2 | 3 | 4 | ## Properties 5 | 6 | Name | Type | Description | Notes 7 | ------------ | ------------- | ------------- | ------------- 8 | 9 | ## Example 10 | 11 | ```python 12 | from openapi_client.models.message_content import MessageContent 13 | 14 | # TODO update the JSON string below 15 | json = "{}" 16 | # create an instance of MessageContent from a JSON string 17 | message_content_instance = MessageContent.from_json(json) 18 | # print the JSON string representation of the object 19 | print(MessageContent.to_json()) 20 | 21 | # convert the object into a dict 22 | message_content_dict = message_content_instance.to_dict() 23 | # create an instance of MessageContent from a dict 24 | message_content_from_dict = MessageContent.from_dict(message_content_dict) 25 | ``` 26 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 27 | 28 | 29 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/docs/Metadata.md: -------------------------------------------------------------------------------- 1 | # Metadata 2 | 3 | 4 | ## Properties 5 | 6 | Name | Type | Description | Notes 7 | ------------ | ------------- | ------------- | ------------- 8 | **model** | **str** | The name of the model used to generate the response. | 9 | **tokens** | **str** | The number of tokens used to generate the response. | [optional] 10 | 11 | ## Example 12 | 13 | ```python 14 | from openapi_client.models.metadata import Metadata 15 | 16 | # TODO update the JSON string below 17 | json = "{}" 18 | # create an instance of Metadata from a JSON string 19 | metadata_instance = Metadata.from_json(json) 20 | # print the JSON string representation of the object 21 | print(Metadata.to_json()) 22 | 23 | # convert the object into a dict 24 | metadata_dict = metadata_instance.to_dict() 25 | # create an instance of Metadata from a dict 26 | metadata_from_dict = Metadata.from_dict(metadata_dict) 27 | ``` 28 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 29 | 30 | 31 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/docs/ResponseMessage.md: -------------------------------------------------------------------------------- 1 | # ResponseMessage 2 | 3 | 4 | ## Properties 5 | 6 | Name | Type | Description | Notes 7 | ------------ | ------------- | ------------- | ------------- 8 | **content** | **str** | The content of the response message. | 9 | **refusal** | **str** | The refusal message if applicable, or null if not. | [optional] 10 | **role** | **str** | The role of the author of the message (e.g., 'assistant'). | 11 | **metadata** | [**Metadata**](Metadata.md) | | [optional] 12 | 13 | ## Example 14 | 15 | ```python 16 | from openapi_client.models.response_message import ResponseMessage 17 | 18 | # TODO update the JSON string below 19 | json = "{}" 20 | # create an instance of ResponseMessage from a JSON string 21 | response_message_instance = ResponseMessage.from_json(json) 22 | # print the JSON string representation of the object 23 | print(ResponseMessage.to_json()) 24 | 25 | # convert the object into a dict 26 | response_message_dict = response_message_instance.to_dict() 27 | # create an instance of ResponseMessage from a dict 28 | response_message_from_dict = ResponseMessage.from_dict(response_message_dict) 29 | ``` 30 | [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) 31 | 32 | 33 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/git_push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ref: https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/ 3 | # 4 | # Usage example: /bin/sh ./git_push.sh wing328 openapi-petstore-perl "minor update" "gitlab.com" 5 | 6 | git_user_id=$1 7 | git_repo_id=$2 8 | release_note=$3 9 | git_host=$4 10 | 11 | if [ "$git_host" = "" ]; then 12 | git_host="github.com" 13 | echo "[INFO] No command line input provided. Set \$git_host to $git_host" 14 | fi 15 | 16 | if [ "$git_user_id" = "" ]; then 17 | git_user_id="GIT_USER_ID" 18 | echo "[INFO] No command line input provided. Set \$git_user_id to $git_user_id" 19 | fi 20 | 21 | if [ "$git_repo_id" = "" ]; then 22 | git_repo_id="GIT_REPO_ID" 23 | echo "[INFO] No command line input provided. Set \$git_repo_id to $git_repo_id" 24 | fi 25 | 26 | if [ "$release_note" = "" ]; then 27 | release_note="Minor update" 28 | echo "[INFO] No command line input provided. Set \$release_note to $release_note" 29 | fi 30 | 31 | # Initialize the local directory as a Git repository 32 | git init 33 | 34 | # Adds the files in the local repository and stages them for commit. 35 | git add . 36 | 37 | # Commits the tracked changes and prepares them to be pushed to a remote repository. 38 | git commit -m "$release_note" 39 | 40 | # Sets the new remote 41 | git_remote=$(git remote) 42 | if [ "$git_remote" = "" ]; then # git remote not defined 43 | 44 | if [ "$GIT_TOKEN" = "" ]; then 45 | echo "[INFO] \$GIT_TOKEN (environment variable) is not set. Using the git credential in your environment." 46 | git remote add origin https://${git_host}/${git_user_id}/${git_repo_id}.git 47 | else 48 | git remote add origin https://${git_user_id}:"${GIT_TOKEN}"@${git_host}/${git_user_id}/${git_repo_id}.git 49 | fi 50 | 51 | fi 52 | 53 | git pull origin master 54 | 55 | # Pushes (Forces) the changes in the local repository up to the remote repository 56 | echo "Git pushing to https://${git_host}/${git_user_id}/${git_repo_id}.git" 57 | git push origin master 2>&1 | grep -v 'To https' 58 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # flake8: noqa 4 | 5 | """ 6 | Open Message Format 7 | 8 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 9 | 10 | The version of the OpenAPI document: 1.0.0 11 | Generated by OpenAPI Generator (https://openapi-generator.tech) 12 | 13 | Do not edit the class manually. 14 | """ # noqa: E501 15 | 16 | 17 | __version__ = "1.0.0" 18 | 19 | # import apis into sdk package 20 | from openapi_client.api.default_api import DefaultApi 21 | 22 | # import ApiClient 23 | from openapi_client.api_response import ApiResponse 24 | from openapi_client.api_client import ApiClient 25 | from openapi_client.configuration import Configuration 26 | from openapi_client.exceptions import OpenApiException 27 | from openapi_client.exceptions import ApiTypeError 28 | from openapi_client.exceptions import ApiValueError 29 | from openapi_client.exceptions import ApiKeyError 30 | from openapi_client.exceptions import ApiAttributeError 31 | from openapi_client.exceptions import ApiException 32 | 33 | # import models into sdk package 34 | from openapi_client.models.content_item import ContentItem 35 | from openapi_client.models.message import Message 36 | from openapi_client.models.message_content import MessageContent 37 | from openapi_client.models.metadata import Metadata 38 | from openapi_client.models.response_message import ResponseMessage 39 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/api/__init__.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # import apis into api package 4 | from openapi_client.api.default_api import DefaultApi 5 | 6 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/api/default_api.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | import warnings 15 | from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt 16 | from typing import Any, Dict, List, Optional, Tuple, Union 17 | from typing_extensions import Annotated 18 | 19 | from typing import List 20 | from openapi_client.models.message import Message 21 | from openapi_client.models.response_message import ResponseMessage 22 | 23 | from openapi_client.api_client import ApiClient, RequestSerialized 24 | from openapi_client.api_response import ApiResponse 25 | from openapi_client.rest import RESTResponseType 26 | 27 | 28 | class DefaultApi: 29 | """NOTE: This class is auto generated by OpenAPI Generator 30 | Ref: https://openapi-generator.tech 31 | 32 | Do not edit the class manually. 33 | """ 34 | 35 | def __init__(self, api_client=None) -> None: 36 | if api_client is None: 37 | api_client = ApiClient.get_default() 38 | self.api_client = api_client 39 | 40 | 41 | @validate_call 42 | def message_post( 43 | self, 44 | message: List[Message], 45 | _request_timeout: Union[ 46 | None, 47 | Annotated[StrictFloat, Field(gt=0)], 48 | Tuple[ 49 | Annotated[StrictFloat, Field(gt=0)], 50 | Annotated[StrictFloat, Field(gt=0)] 51 | ] 52 | ] = None, 53 | _request_auth: Optional[Dict[StrictStr, Any]] = None, 54 | _content_type: Optional[StrictStr] = None, 55 | _headers: Optional[Dict[StrictStr, Any]] = None, 56 | _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, 57 | ) -> ResponseMessage: 58 | """This method allows developers to receive chat messages from an upstream client in a standard format. 59 | 60 | This endpoint receives a message or messages object from a client and returns a message containing the content of the response. 61 | 62 | :param message: (required) 63 | :type message: List[Message] 64 | :param _request_timeout: timeout setting for this request. If one 65 | number provided, it will be total request 66 | timeout. It can also be a pair (tuple) of 67 | (connection, read) timeouts. 68 | :type _request_timeout: int, tuple(int, int), optional 69 | :param _request_auth: set to override the auth_settings for an a single 70 | request; this effectively ignores the 71 | authentication in the spec for a single request. 72 | :type _request_auth: dict, optional 73 | :param _content_type: force content-type for the request. 74 | :type _content_type: str, Optional 75 | :param _headers: set to override the headers for a single 76 | request; this effectively ignores the headers 77 | in the spec for a single request. 78 | :type _headers: dict, optional 79 | :param _host_index: set to override the host_index for a single 80 | request; this effectively ignores the host_index 81 | in the spec for a single request. 82 | :type _host_index: int, optional 83 | :return: Returns the result object. 84 | """ # noqa: E501 85 | 86 | _param = self._message_post_serialize( 87 | message=message, 88 | _request_auth=_request_auth, 89 | _content_type=_content_type, 90 | _headers=_headers, 91 | _host_index=_host_index 92 | ) 93 | 94 | _response_types_map: Dict[str, Optional[str]] = { 95 | '200': "ResponseMessage", 96 | '400': None, 97 | '500': None, 98 | } 99 | response_data = self.api_client.call_api( 100 | *_param, 101 | _request_timeout=_request_timeout 102 | ) 103 | response_data.read() 104 | return self.api_client.response_deserialize( 105 | response_data=response_data, 106 | response_types_map=_response_types_map, 107 | ).data 108 | 109 | 110 | @validate_call 111 | def message_post_with_http_info( 112 | self, 113 | message: List[Message], 114 | _request_timeout: Union[ 115 | None, 116 | Annotated[StrictFloat, Field(gt=0)], 117 | Tuple[ 118 | Annotated[StrictFloat, Field(gt=0)], 119 | Annotated[StrictFloat, Field(gt=0)] 120 | ] 121 | ] = None, 122 | _request_auth: Optional[Dict[StrictStr, Any]] = None, 123 | _content_type: Optional[StrictStr] = None, 124 | _headers: Optional[Dict[StrictStr, Any]] = None, 125 | _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, 126 | ) -> ApiResponse[ResponseMessage]: 127 | """This method allows developers to receive chat messages from an upstream client in a standard format. 128 | 129 | This endpoint receives a message or messages object from a client and returns a message containing the content of the response. 130 | 131 | :param message: (required) 132 | :type message: List[Message] 133 | :param _request_timeout: timeout setting for this request. If one 134 | number provided, it will be total request 135 | timeout. It can also be a pair (tuple) of 136 | (connection, read) timeouts. 137 | :type _request_timeout: int, tuple(int, int), optional 138 | :param _request_auth: set to override the auth_settings for an a single 139 | request; this effectively ignores the 140 | authentication in the spec for a single request. 141 | :type _request_auth: dict, optional 142 | :param _content_type: force content-type for the request. 143 | :type _content_type: str, Optional 144 | :param _headers: set to override the headers for a single 145 | request; this effectively ignores the headers 146 | in the spec for a single request. 147 | :type _headers: dict, optional 148 | :param _host_index: set to override the host_index for a single 149 | request; this effectively ignores the host_index 150 | in the spec for a single request. 151 | :type _host_index: int, optional 152 | :return: Returns the result object. 153 | """ # noqa: E501 154 | 155 | _param = self._message_post_serialize( 156 | message=message, 157 | _request_auth=_request_auth, 158 | _content_type=_content_type, 159 | _headers=_headers, 160 | _host_index=_host_index 161 | ) 162 | 163 | _response_types_map: Dict[str, Optional[str]] = { 164 | '200': "ResponseMessage", 165 | '400': None, 166 | '500': None, 167 | } 168 | response_data = self.api_client.call_api( 169 | *_param, 170 | _request_timeout=_request_timeout 171 | ) 172 | response_data.read() 173 | return self.api_client.response_deserialize( 174 | response_data=response_data, 175 | response_types_map=_response_types_map, 176 | ) 177 | 178 | 179 | @validate_call 180 | def message_post_without_preload_content( 181 | self, 182 | message: List[Message], 183 | _request_timeout: Union[ 184 | None, 185 | Annotated[StrictFloat, Field(gt=0)], 186 | Tuple[ 187 | Annotated[StrictFloat, Field(gt=0)], 188 | Annotated[StrictFloat, Field(gt=0)] 189 | ] 190 | ] = None, 191 | _request_auth: Optional[Dict[StrictStr, Any]] = None, 192 | _content_type: Optional[StrictStr] = None, 193 | _headers: Optional[Dict[StrictStr, Any]] = None, 194 | _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, 195 | ) -> RESTResponseType: 196 | """This method allows developers to receive chat messages from an upstream client in a standard format. 197 | 198 | This endpoint receives a message or messages object from a client and returns a message containing the content of the response. 199 | 200 | :param message: (required) 201 | :type message: List[Message] 202 | :param _request_timeout: timeout setting for this request. If one 203 | number provided, it will be total request 204 | timeout. It can also be a pair (tuple) of 205 | (connection, read) timeouts. 206 | :type _request_timeout: int, tuple(int, int), optional 207 | :param _request_auth: set to override the auth_settings for an a single 208 | request; this effectively ignores the 209 | authentication in the spec for a single request. 210 | :type _request_auth: dict, optional 211 | :param _content_type: force content-type for the request. 212 | :type _content_type: str, Optional 213 | :param _headers: set to override the headers for a single 214 | request; this effectively ignores the headers 215 | in the spec for a single request. 216 | :type _headers: dict, optional 217 | :param _host_index: set to override the host_index for a single 218 | request; this effectively ignores the host_index 219 | in the spec for a single request. 220 | :type _host_index: int, optional 221 | :return: Returns the result object. 222 | """ # noqa: E501 223 | 224 | _param = self._message_post_serialize( 225 | message=message, 226 | _request_auth=_request_auth, 227 | _content_type=_content_type, 228 | _headers=_headers, 229 | _host_index=_host_index 230 | ) 231 | 232 | _response_types_map: Dict[str, Optional[str]] = { 233 | '200': "ResponseMessage", 234 | '400': None, 235 | '500': None, 236 | } 237 | response_data = self.api_client.call_api( 238 | *_param, 239 | _request_timeout=_request_timeout 240 | ) 241 | return response_data.response 242 | 243 | 244 | def _message_post_serialize( 245 | self, 246 | message, 247 | _request_auth, 248 | _content_type, 249 | _headers, 250 | _host_index, 251 | ) -> RequestSerialized: 252 | 253 | _host = None 254 | 255 | _collection_formats: Dict[str, str] = { 256 | 'Message': '', 257 | } 258 | 259 | _path_params: Dict[str, str] = {} 260 | _query_params: List[Tuple[str, str]] = [] 261 | _header_params: Dict[str, Optional[str]] = _headers or {} 262 | _form_params: List[Tuple[str, str]] = [] 263 | _files: Dict[str, Union[str, bytes]] = {} 264 | _body_params: Optional[bytes] = None 265 | 266 | # process the path parameters 267 | # process the query parameters 268 | # process the header parameters 269 | # process the form parameters 270 | # process the body parameter 271 | if message is not None: 272 | _body_params = message 273 | 274 | 275 | # set the HTTP header `Accept` 276 | if 'Accept' not in _header_params: 277 | _header_params['Accept'] = self.api_client.select_header_accept( 278 | [ 279 | 'application/json' 280 | ] 281 | ) 282 | 283 | # set the HTTP header `Content-Type` 284 | if _content_type: 285 | _header_params['Content-Type'] = _content_type 286 | else: 287 | _default_content_type = ( 288 | self.api_client.select_header_content_type( 289 | [ 290 | 'application/json' 291 | ] 292 | ) 293 | ) 294 | if _default_content_type is not None: 295 | _header_params['Content-Type'] = _default_content_type 296 | 297 | # authentication setting 298 | _auth_settings: List[str] = [ 299 | ] 300 | 301 | return self.api_client.param_serialize( 302 | method='POST', 303 | resource_path='/message', 304 | path_params=_path_params, 305 | query_params=_query_params, 306 | header_params=_header_params, 307 | body=_body_params, 308 | post_params=_form_params, 309 | files=_files, 310 | auth_settings=_auth_settings, 311 | collection_formats=_collection_formats, 312 | _host=_host, 313 | _request_auth=_request_auth 314 | ) 315 | 316 | 317 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/api_client.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import datetime 16 | from dateutil.parser import parse 17 | from enum import Enum 18 | import decimal 19 | import json 20 | import mimetypes 21 | import os 22 | import re 23 | import tempfile 24 | 25 | from urllib.parse import quote 26 | from typing import Tuple, Optional, List, Dict, Union 27 | from pydantic import SecretStr 28 | 29 | from openapi_client.configuration import Configuration 30 | from openapi_client.api_response import ApiResponse, T as ApiResponseT 31 | import openapi_client.models 32 | from openapi_client import rest 33 | from openapi_client.exceptions import ( 34 | ApiValueError, 35 | ApiException, 36 | BadRequestException, 37 | UnauthorizedException, 38 | ForbiddenException, 39 | NotFoundException, 40 | ServiceException 41 | ) 42 | 43 | RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]] 44 | 45 | class ApiClient: 46 | """Generic API client for OpenAPI client library builds. 47 | 48 | OpenAPI generic API client. This client handles the client- 49 | server communication, and is invariant across implementations. Specifics of 50 | the methods and models for each application are generated from the OpenAPI 51 | templates. 52 | 53 | :param configuration: .Configuration object for this client 54 | :param header_name: a header to pass when making calls to the API. 55 | :param header_value: a header value to pass when making calls to 56 | the API. 57 | :param cookie: a cookie to include in the header when making calls 58 | to the API 59 | """ 60 | 61 | PRIMITIVE_TYPES = (float, bool, bytes, str, int) 62 | NATIVE_TYPES_MAPPING = { 63 | 'int': int, 64 | 'long': int, # TODO remove as only py3 is supported? 65 | 'float': float, 66 | 'str': str, 67 | 'bool': bool, 68 | 'date': datetime.date, 69 | 'datetime': datetime.datetime, 70 | 'decimal': decimal.Decimal, 71 | 'object': object, 72 | } 73 | _pool = None 74 | 75 | def __init__( 76 | self, 77 | configuration=None, 78 | header_name=None, 79 | header_value=None, 80 | cookie=None 81 | ) -> None: 82 | # use default configuration if none is provided 83 | if configuration is None: 84 | configuration = Configuration.get_default() 85 | self.configuration = configuration 86 | 87 | self.rest_client = rest.RESTClientObject(configuration) 88 | self.default_headers = {} 89 | if header_name is not None: 90 | self.default_headers[header_name] = header_value 91 | self.cookie = cookie 92 | # Set default User-Agent. 93 | self.user_agent = 'OpenAPI-Generator/1.0.0/python' 94 | self.client_side_validation = configuration.client_side_validation 95 | 96 | def __enter__(self): 97 | return self 98 | 99 | def __exit__(self, exc_type, exc_value, traceback): 100 | pass 101 | 102 | @property 103 | def user_agent(self): 104 | """User agent for this API client""" 105 | return self.default_headers['User-Agent'] 106 | 107 | @user_agent.setter 108 | def user_agent(self, value): 109 | self.default_headers['User-Agent'] = value 110 | 111 | def set_default_header(self, header_name, header_value): 112 | self.default_headers[header_name] = header_value 113 | 114 | 115 | _default = None 116 | 117 | @classmethod 118 | def get_default(cls): 119 | """Return new instance of ApiClient. 120 | 121 | This method returns newly created, based on default constructor, 122 | object of ApiClient class or returns a copy of default 123 | ApiClient. 124 | 125 | :return: The ApiClient object. 126 | """ 127 | if cls._default is None: 128 | cls._default = ApiClient() 129 | return cls._default 130 | 131 | @classmethod 132 | def set_default(cls, default): 133 | """Set default instance of ApiClient. 134 | 135 | It stores default ApiClient. 136 | 137 | :param default: object of ApiClient. 138 | """ 139 | cls._default = default 140 | 141 | def param_serialize( 142 | self, 143 | method, 144 | resource_path, 145 | path_params=None, 146 | query_params=None, 147 | header_params=None, 148 | body=None, 149 | post_params=None, 150 | files=None, auth_settings=None, 151 | collection_formats=None, 152 | _host=None, 153 | _request_auth=None 154 | ) -> RequestSerialized: 155 | 156 | """Builds the HTTP request params needed by the request. 157 | :param method: Method to call. 158 | :param resource_path: Path to method endpoint. 159 | :param path_params: Path parameters in the url. 160 | :param query_params: Query parameters in the url. 161 | :param header_params: Header parameters to be 162 | placed in the request header. 163 | :param body: Request body. 164 | :param post_params dict: Request post form parameters, 165 | for `application/x-www-form-urlencoded`, `multipart/form-data`. 166 | :param auth_settings list: Auth Settings names for the request. 167 | :param files dict: key -> filename, value -> filepath, 168 | for `multipart/form-data`. 169 | :param collection_formats: dict of collection formats for path, query, 170 | header, and post parameters. 171 | :param _request_auth: set to override the auth_settings for an a single 172 | request; this effectively ignores the authentication 173 | in the spec for a single request. 174 | :return: tuple of form (path, http_method, query_params, header_params, 175 | body, post_params, files) 176 | """ 177 | 178 | config = self.configuration 179 | 180 | # header parameters 181 | header_params = header_params or {} 182 | header_params.update(self.default_headers) 183 | if self.cookie: 184 | header_params['Cookie'] = self.cookie 185 | if header_params: 186 | header_params = self.sanitize_for_serialization(header_params) 187 | header_params = dict( 188 | self.parameters_to_tuples(header_params,collection_formats) 189 | ) 190 | 191 | # path parameters 192 | if path_params: 193 | path_params = self.sanitize_for_serialization(path_params) 194 | path_params = self.parameters_to_tuples( 195 | path_params, 196 | collection_formats 197 | ) 198 | for k, v in path_params: 199 | # specified safe chars, encode everything 200 | resource_path = resource_path.replace( 201 | '{%s}' % k, 202 | quote(str(v), safe=config.safe_chars_for_path_param) 203 | ) 204 | 205 | # post parameters 206 | if post_params or files: 207 | post_params = post_params if post_params else [] 208 | post_params = self.sanitize_for_serialization(post_params) 209 | post_params = self.parameters_to_tuples( 210 | post_params, 211 | collection_formats 212 | ) 213 | if files: 214 | post_params.extend(self.files_parameters(files)) 215 | 216 | # auth setting 217 | self.update_params_for_auth( 218 | header_params, 219 | query_params, 220 | auth_settings, 221 | resource_path, 222 | method, 223 | body, 224 | request_auth=_request_auth 225 | ) 226 | 227 | # body 228 | if body: 229 | body = self.sanitize_for_serialization(body) 230 | 231 | # request url 232 | if _host is None or self.configuration.ignore_operation_servers: 233 | url = self.configuration.host + resource_path 234 | else: 235 | # use server/host defined in path or operation instead 236 | url = _host + resource_path 237 | 238 | # query parameters 239 | if query_params: 240 | query_params = self.sanitize_for_serialization(query_params) 241 | url_query = self.parameters_to_url_query( 242 | query_params, 243 | collection_formats 244 | ) 245 | url += "?" + url_query 246 | 247 | return method, url, header_params, body, post_params 248 | 249 | 250 | def call_api( 251 | self, 252 | method, 253 | url, 254 | header_params=None, 255 | body=None, 256 | post_params=None, 257 | _request_timeout=None 258 | ) -> rest.RESTResponse: 259 | """Makes the HTTP request (synchronous) 260 | :param method: Method to call. 261 | :param url: Path to method endpoint. 262 | :param header_params: Header parameters to be 263 | placed in the request header. 264 | :param body: Request body. 265 | :param post_params dict: Request post form parameters, 266 | for `application/x-www-form-urlencoded`, `multipart/form-data`. 267 | :param _request_timeout: timeout setting for this request. 268 | :return: RESTResponse 269 | """ 270 | 271 | try: 272 | # perform request and return response 273 | response_data = self.rest_client.request( 274 | method, url, 275 | headers=header_params, 276 | body=body, post_params=post_params, 277 | _request_timeout=_request_timeout 278 | ) 279 | 280 | except ApiException as e: 281 | raise e 282 | 283 | return response_data 284 | 285 | def response_deserialize( 286 | self, 287 | response_data: rest.RESTResponse, 288 | response_types_map: Optional[Dict[str, ApiResponseT]]=None 289 | ) -> ApiResponse[ApiResponseT]: 290 | """Deserializes response into an object. 291 | :param response_data: RESTResponse object to be deserialized. 292 | :param response_types_map: dict of response types. 293 | :return: ApiResponse 294 | """ 295 | 296 | msg = "RESTResponse.read() must be called before passing it to response_deserialize()" 297 | assert response_data.data is not None, msg 298 | 299 | response_type = response_types_map.get(str(response_data.status), None) 300 | if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599: 301 | # if not found, look for '1XX', '2XX', etc. 302 | response_type = response_types_map.get(str(response_data.status)[0] + "XX", None) 303 | 304 | # deserialize response data 305 | response_text = None 306 | return_data = None 307 | try: 308 | if response_type == "bytearray": 309 | return_data = response_data.data 310 | elif response_type == "file": 311 | return_data = self.__deserialize_file(response_data) 312 | elif response_type is not None: 313 | match = None 314 | content_type = response_data.getheader('content-type') 315 | if content_type is not None: 316 | match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type) 317 | encoding = match.group(1) if match else "utf-8" 318 | response_text = response_data.data.decode(encoding) 319 | return_data = self.deserialize(response_text, response_type, content_type) 320 | finally: 321 | if not 200 <= response_data.status <= 299: 322 | raise ApiException.from_response( 323 | http_resp=response_data, 324 | body=response_text, 325 | data=return_data, 326 | ) 327 | 328 | return ApiResponse( 329 | status_code = response_data.status, 330 | data = return_data, 331 | headers = response_data.getheaders(), 332 | raw_data = response_data.data 333 | ) 334 | 335 | def sanitize_for_serialization(self, obj): 336 | """Builds a JSON POST object. 337 | 338 | If obj is None, return None. 339 | If obj is SecretStr, return obj.get_secret_value() 340 | If obj is str, int, long, float, bool, return directly. 341 | If obj is datetime.datetime, datetime.date 342 | convert to string in iso8601 format. 343 | If obj is decimal.Decimal return string representation. 344 | If obj is list, sanitize each element in the list. 345 | If obj is dict, return the dict. 346 | If obj is OpenAPI model, return the properties dict. 347 | 348 | :param obj: The data to serialize. 349 | :return: The serialized form of data. 350 | """ 351 | if obj is None: 352 | return None 353 | elif isinstance(obj, Enum): 354 | return obj.value 355 | elif isinstance(obj, SecretStr): 356 | return obj.get_secret_value() 357 | elif isinstance(obj, self.PRIMITIVE_TYPES): 358 | return obj 359 | elif isinstance(obj, list): 360 | return [ 361 | self.sanitize_for_serialization(sub_obj) for sub_obj in obj 362 | ] 363 | elif isinstance(obj, tuple): 364 | return tuple( 365 | self.sanitize_for_serialization(sub_obj) for sub_obj in obj 366 | ) 367 | elif isinstance(obj, (datetime.datetime, datetime.date)): 368 | return obj.isoformat() 369 | elif isinstance(obj, decimal.Decimal): 370 | return str(obj) 371 | 372 | elif isinstance(obj, dict): 373 | obj_dict = obj 374 | else: 375 | # Convert model obj to dict except 376 | # attributes `openapi_types`, `attribute_map` 377 | # and attributes which value is not None. 378 | # Convert attribute name to json key in 379 | # model definition for request. 380 | if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')): 381 | obj_dict = obj.to_dict() 382 | else: 383 | obj_dict = obj.__dict__ 384 | 385 | return { 386 | key: self.sanitize_for_serialization(val) 387 | for key, val in obj_dict.items() 388 | } 389 | 390 | def deserialize(self, response_text: str, response_type: str, content_type: Optional[str]): 391 | """Deserializes response into an object. 392 | 393 | :param response: RESTResponse object to be deserialized. 394 | :param response_type: class literal for 395 | deserialized object, or string of class name. 396 | :param content_type: content type of response. 397 | 398 | :return: deserialized object. 399 | """ 400 | 401 | # fetch data from response object 402 | if content_type is None: 403 | try: 404 | data = json.loads(response_text) 405 | except ValueError: 406 | data = response_text 407 | elif content_type.startswith("application/json"): 408 | if response_text == "": 409 | data = "" 410 | else: 411 | data = json.loads(response_text) 412 | elif content_type.startswith("text/plain"): 413 | data = response_text 414 | else: 415 | raise ApiException( 416 | status=0, 417 | reason="Unsupported content type: {0}".format(content_type) 418 | ) 419 | 420 | return self.__deserialize(data, response_type) 421 | 422 | def __deserialize(self, data, klass): 423 | """Deserializes dict, list, str into an object. 424 | 425 | :param data: dict, list or str. 426 | :param klass: class literal, or string of class name. 427 | 428 | :return: object. 429 | """ 430 | if data is None: 431 | return None 432 | 433 | if isinstance(klass, str): 434 | if klass.startswith('List['): 435 | m = re.match(r'List\[(.*)]', klass) 436 | assert m is not None, "Malformed List type definition" 437 | sub_kls = m.group(1) 438 | return [self.__deserialize(sub_data, sub_kls) 439 | for sub_data in data] 440 | 441 | if klass.startswith('Dict['): 442 | m = re.match(r'Dict\[([^,]*), (.*)]', klass) 443 | assert m is not None, "Malformed Dict type definition" 444 | sub_kls = m.group(2) 445 | return {k: self.__deserialize(v, sub_kls) 446 | for k, v in data.items()} 447 | 448 | # convert str to class 449 | if klass in self.NATIVE_TYPES_MAPPING: 450 | klass = self.NATIVE_TYPES_MAPPING[klass] 451 | else: 452 | klass = getattr(openapi_client.models, klass) 453 | 454 | if klass in self.PRIMITIVE_TYPES: 455 | return self.__deserialize_primitive(data, klass) 456 | elif klass == object: 457 | return self.__deserialize_object(data) 458 | elif klass == datetime.date: 459 | return self.__deserialize_date(data) 460 | elif klass == datetime.datetime: 461 | return self.__deserialize_datetime(data) 462 | elif klass == decimal.Decimal: 463 | return decimal.Decimal(data) 464 | elif issubclass(klass, Enum): 465 | return self.__deserialize_enum(data, klass) 466 | else: 467 | return self.__deserialize_model(data, klass) 468 | 469 | def parameters_to_tuples(self, params, collection_formats): 470 | """Get parameters as list of tuples, formatting collections. 471 | 472 | :param params: Parameters as dict or list of two-tuples 473 | :param dict collection_formats: Parameter collection formats 474 | :return: Parameters as list of tuples, collections formatted 475 | """ 476 | new_params: List[Tuple[str, str]] = [] 477 | if collection_formats is None: 478 | collection_formats = {} 479 | for k, v in params.items() if isinstance(params, dict) else params: 480 | if k in collection_formats: 481 | collection_format = collection_formats[k] 482 | if collection_format == 'multi': 483 | new_params.extend((k, value) for value in v) 484 | else: 485 | if collection_format == 'ssv': 486 | delimiter = ' ' 487 | elif collection_format == 'tsv': 488 | delimiter = '\t' 489 | elif collection_format == 'pipes': 490 | delimiter = '|' 491 | else: # csv is the default 492 | delimiter = ',' 493 | new_params.append( 494 | (k, delimiter.join(str(value) for value in v))) 495 | else: 496 | new_params.append((k, v)) 497 | return new_params 498 | 499 | def parameters_to_url_query(self, params, collection_formats): 500 | """Get parameters as list of tuples, formatting collections. 501 | 502 | :param params: Parameters as dict or list of two-tuples 503 | :param dict collection_formats: Parameter collection formats 504 | :return: URL query string (e.g. a=Hello%20World&b=123) 505 | """ 506 | new_params: List[Tuple[str, str]] = [] 507 | if collection_formats is None: 508 | collection_formats = {} 509 | for k, v in params.items() if isinstance(params, dict) else params: 510 | if isinstance(v, bool): 511 | v = str(v).lower() 512 | if isinstance(v, (int, float)): 513 | v = str(v) 514 | if isinstance(v, dict): 515 | v = json.dumps(v) 516 | 517 | if k in collection_formats: 518 | collection_format = collection_formats[k] 519 | if collection_format == 'multi': 520 | new_params.extend((k, str(value)) for value in v) 521 | else: 522 | if collection_format == 'ssv': 523 | delimiter = ' ' 524 | elif collection_format == 'tsv': 525 | delimiter = '\t' 526 | elif collection_format == 'pipes': 527 | delimiter = '|' 528 | else: # csv is the default 529 | delimiter = ',' 530 | new_params.append( 531 | (k, delimiter.join(quote(str(value)) for value in v)) 532 | ) 533 | else: 534 | new_params.append((k, quote(str(v)))) 535 | 536 | return "&".join(["=".join(map(str, item)) for item in new_params]) 537 | 538 | def files_parameters(self, files: Dict[str, Union[str, bytes]]): 539 | """Builds form parameters. 540 | 541 | :param files: File parameters. 542 | :return: Form parameters with files. 543 | """ 544 | params = [] 545 | for k, v in files.items(): 546 | if isinstance(v, str): 547 | with open(v, 'rb') as f: 548 | filename = os.path.basename(f.name) 549 | filedata = f.read() 550 | elif isinstance(v, bytes): 551 | filename = k 552 | filedata = v 553 | else: 554 | raise ValueError("Unsupported file value") 555 | mimetype = ( 556 | mimetypes.guess_type(filename)[0] 557 | or 'application/octet-stream' 558 | ) 559 | params.append( 560 | tuple([k, tuple([filename, filedata, mimetype])]) 561 | ) 562 | return params 563 | 564 | def select_header_accept(self, accepts: List[str]) -> Optional[str]: 565 | """Returns `Accept` based on an array of accepts provided. 566 | 567 | :param accepts: List of headers. 568 | :return: Accept (e.g. application/json). 569 | """ 570 | if not accepts: 571 | return None 572 | 573 | for accept in accepts: 574 | if re.search('json', accept, re.IGNORECASE): 575 | return accept 576 | 577 | return accepts[0] 578 | 579 | def select_header_content_type(self, content_types): 580 | """Returns `Content-Type` based on an array of content_types provided. 581 | 582 | :param content_types: List of content-types. 583 | :return: Content-Type (e.g. application/json). 584 | """ 585 | if not content_types: 586 | return None 587 | 588 | for content_type in content_types: 589 | if re.search('json', content_type, re.IGNORECASE): 590 | return content_type 591 | 592 | return content_types[0] 593 | 594 | def update_params_for_auth( 595 | self, 596 | headers, 597 | queries, 598 | auth_settings, 599 | resource_path, 600 | method, 601 | body, 602 | request_auth=None 603 | ) -> None: 604 | """Updates header and query params based on authentication setting. 605 | 606 | :param headers: Header parameters dict to be updated. 607 | :param queries: Query parameters tuple list to be updated. 608 | :param auth_settings: Authentication setting identifiers list. 609 | :resource_path: A string representation of the HTTP request resource path. 610 | :method: A string representation of the HTTP request method. 611 | :body: A object representing the body of the HTTP request. 612 | The object type is the return value of sanitize_for_serialization(). 613 | :param request_auth: if set, the provided settings will 614 | override the token in the configuration. 615 | """ 616 | if not auth_settings: 617 | return 618 | 619 | if request_auth: 620 | self._apply_auth_params( 621 | headers, 622 | queries, 623 | resource_path, 624 | method, 625 | body, 626 | request_auth 627 | ) 628 | else: 629 | for auth in auth_settings: 630 | auth_setting = self.configuration.auth_settings().get(auth) 631 | if auth_setting: 632 | self._apply_auth_params( 633 | headers, 634 | queries, 635 | resource_path, 636 | method, 637 | body, 638 | auth_setting 639 | ) 640 | 641 | def _apply_auth_params( 642 | self, 643 | headers, 644 | queries, 645 | resource_path, 646 | method, 647 | body, 648 | auth_setting 649 | ) -> None: 650 | """Updates the request parameters based on a single auth_setting 651 | 652 | :param headers: Header parameters dict to be updated. 653 | :param queries: Query parameters tuple list to be updated. 654 | :resource_path: A string representation of the HTTP request resource path. 655 | :method: A string representation of the HTTP request method. 656 | :body: A object representing the body of the HTTP request. 657 | The object type is the return value of sanitize_for_serialization(). 658 | :param auth_setting: auth settings for the endpoint 659 | """ 660 | if auth_setting['in'] == 'cookie': 661 | headers['Cookie'] = auth_setting['value'] 662 | elif auth_setting['in'] == 'header': 663 | if auth_setting['type'] != 'http-signature': 664 | headers[auth_setting['key']] = auth_setting['value'] 665 | elif auth_setting['in'] == 'query': 666 | queries.append((auth_setting['key'], auth_setting['value'])) 667 | else: 668 | raise ApiValueError( 669 | 'Authentication token must be in `query` or `header`' 670 | ) 671 | 672 | def __deserialize_file(self, response): 673 | """Deserializes body to file 674 | 675 | Saves response body into a file in a temporary folder, 676 | using the filename from the `Content-Disposition` header if provided. 677 | 678 | handle file downloading 679 | save response body into a tmp file and return the instance 680 | 681 | :param response: RESTResponse. 682 | :return: file path. 683 | """ 684 | fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path) 685 | os.close(fd) 686 | os.remove(path) 687 | 688 | content_disposition = response.getheader("Content-Disposition") 689 | if content_disposition: 690 | m = re.search( 691 | r'filename=[\'"]?([^\'"\s]+)[\'"]?', 692 | content_disposition 693 | ) 694 | assert m is not None, "Unexpected 'content-disposition' header value" 695 | filename = m.group(1) 696 | path = os.path.join(os.path.dirname(path), filename) 697 | 698 | with open(path, "wb") as f: 699 | f.write(response.data) 700 | 701 | return path 702 | 703 | def __deserialize_primitive(self, data, klass): 704 | """Deserializes string to primitive type. 705 | 706 | :param data: str. 707 | :param klass: class literal. 708 | 709 | :return: int, long, float, str, bool. 710 | """ 711 | try: 712 | return klass(data) 713 | except UnicodeEncodeError: 714 | return str(data) 715 | except TypeError: 716 | return data 717 | 718 | def __deserialize_object(self, value): 719 | """Return an original value. 720 | 721 | :return: object. 722 | """ 723 | return value 724 | 725 | def __deserialize_date(self, string): 726 | """Deserializes string to date. 727 | 728 | :param string: str. 729 | :return: date. 730 | """ 731 | try: 732 | return parse(string).date() 733 | except ImportError: 734 | return string 735 | except ValueError: 736 | raise rest.ApiException( 737 | status=0, 738 | reason="Failed to parse `{0}` as date object".format(string) 739 | ) 740 | 741 | def __deserialize_datetime(self, string): 742 | """Deserializes string to datetime. 743 | 744 | The string should be in iso8601 datetime format. 745 | 746 | :param string: str. 747 | :return: datetime. 748 | """ 749 | try: 750 | return parse(string) 751 | except ImportError: 752 | return string 753 | except ValueError: 754 | raise rest.ApiException( 755 | status=0, 756 | reason=( 757 | "Failed to parse `{0}` as datetime object" 758 | .format(string) 759 | ) 760 | ) 761 | 762 | def __deserialize_enum(self, data, klass): 763 | """Deserializes primitive type to enum. 764 | 765 | :param data: primitive type. 766 | :param klass: class literal. 767 | :return: enum value. 768 | """ 769 | try: 770 | return klass(data) 771 | except ValueError: 772 | raise rest.ApiException( 773 | status=0, 774 | reason=( 775 | "Failed to parse `{0}` as `{1}`" 776 | .format(data, klass) 777 | ) 778 | ) 779 | 780 | def __deserialize_model(self, data, klass): 781 | """Deserializes list or dict to model. 782 | 783 | :param data: dict, list. 784 | :param klass: class literal. 785 | :return: model object. 786 | """ 787 | 788 | return klass.from_dict(data) 789 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/api_response.py: -------------------------------------------------------------------------------- 1 | """API response object.""" 2 | 3 | from __future__ import annotations 4 | from typing import Optional, Generic, Mapping, TypeVar 5 | from pydantic import Field, StrictInt, StrictBytes, BaseModel 6 | 7 | T = TypeVar("T") 8 | 9 | class ApiResponse(BaseModel, Generic[T]): 10 | """ 11 | API response object 12 | """ 13 | 14 | status_code: StrictInt = Field(description="HTTP status code") 15 | headers: Optional[Mapping[str, str]] = Field(None, description="HTTP headers") 16 | data: T = Field(description="Deserialized data given the data type") 17 | raw_data: StrictBytes = Field(description="Raw data (HTTP response body)") 18 | 19 | model_config = { 20 | "arbitrary_types_allowed": True 21 | } 22 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/configuration.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import copy 16 | import logging 17 | from logging import FileHandler 18 | import multiprocessing 19 | import sys 20 | from typing import Optional 21 | import urllib3 22 | 23 | import http.client as httplib 24 | 25 | JSON_SCHEMA_VALIDATION_KEYWORDS = { 26 | 'multipleOf', 'maximum', 'exclusiveMaximum', 27 | 'minimum', 'exclusiveMinimum', 'maxLength', 28 | 'minLength', 'pattern', 'maxItems', 'minItems' 29 | } 30 | 31 | class Configuration: 32 | """This class contains various settings of the API client. 33 | 34 | :param host: Base url. 35 | :param ignore_operation_servers 36 | Boolean to ignore operation servers for the API client. 37 | Config will use `host` as the base url regardless of the operation servers. 38 | :param api_key: Dict to store API key(s). 39 | Each entry in the dict specifies an API key. 40 | The dict key is the name of the security scheme in the OAS specification. 41 | The dict value is the API key secret. 42 | :param api_key_prefix: Dict to store API prefix (e.g. Bearer). 43 | The dict key is the name of the security scheme in the OAS specification. 44 | The dict value is an API key prefix when generating the auth data. 45 | :param username: Username for HTTP basic authentication. 46 | :param password: Password for HTTP basic authentication. 47 | :param access_token: Access token. 48 | :param server_index: Index to servers configuration. 49 | :param server_variables: Mapping with string values to replace variables in 50 | templated server configuration. The validation of enums is performed for 51 | variables with defined enum values before. 52 | :param server_operation_index: Mapping from operation ID to an index to server 53 | configuration. 54 | :param server_operation_variables: Mapping from operation ID to a mapping with 55 | string values to replace variables in templated server configuration. 56 | The validation of enums is performed for variables with defined enum 57 | values before. 58 | :param ssl_ca_cert: str - the path to a file of concatenated CA certificates 59 | in PEM format. 60 | :param retries: Number of retries for API requests. 61 | 62 | """ 63 | 64 | _default = None 65 | 66 | def __init__(self, host=None, 67 | api_key=None, api_key_prefix=None, 68 | username=None, password=None, 69 | access_token=None, 70 | server_index=None, server_variables=None, 71 | server_operation_index=None, server_operation_variables=None, 72 | ignore_operation_servers=False, 73 | ssl_ca_cert=None, 74 | retries=None, 75 | *, 76 | debug: Optional[bool] = None 77 | ) -> None: 78 | """Constructor 79 | """ 80 | self._base_path = "http://localhost" if host is None else host 81 | """Default Base url 82 | """ 83 | self.server_index = 0 if server_index is None and host is None else server_index 84 | self.server_operation_index = server_operation_index or {} 85 | """Default server index 86 | """ 87 | self.server_variables = server_variables or {} 88 | self.server_operation_variables = server_operation_variables or {} 89 | """Default server variables 90 | """ 91 | self.ignore_operation_servers = ignore_operation_servers 92 | """Ignore operation servers 93 | """ 94 | self.temp_folder_path = None 95 | """Temp file folder for downloading files 96 | """ 97 | # Authentication Settings 98 | self.api_key = {} 99 | if api_key: 100 | self.api_key = api_key 101 | """dict to store API key(s) 102 | """ 103 | self.api_key_prefix = {} 104 | if api_key_prefix: 105 | self.api_key_prefix = api_key_prefix 106 | """dict to store API prefix (e.g. Bearer) 107 | """ 108 | self.refresh_api_key_hook = None 109 | """function hook to refresh API key if expired 110 | """ 111 | self.username = username 112 | """Username for HTTP basic authentication 113 | """ 114 | self.password = password 115 | """Password for HTTP basic authentication 116 | """ 117 | self.access_token = access_token 118 | """Access token 119 | """ 120 | self.logger = {} 121 | """Logging Settings 122 | """ 123 | self.logger["package_logger"] = logging.getLogger("openapi_client") 124 | self.logger["urllib3_logger"] = logging.getLogger("urllib3") 125 | self.logger_format = '%(asctime)s %(levelname)s %(message)s' 126 | """Log format 127 | """ 128 | self.logger_stream_handler = None 129 | """Log stream handler 130 | """ 131 | self.logger_file_handler: Optional[FileHandler] = None 132 | """Log file handler 133 | """ 134 | self.logger_file = None 135 | """Debug file location 136 | """ 137 | if debug is not None: 138 | self.debug = debug 139 | else: 140 | self.__debug = False 141 | """Debug switch 142 | """ 143 | 144 | self.verify_ssl = True 145 | """SSL/TLS verification 146 | Set this to false to skip verifying SSL certificate when calling API 147 | from https server. 148 | """ 149 | self.ssl_ca_cert = ssl_ca_cert 150 | """Set this to customize the certificate file to verify the peer. 151 | """ 152 | self.cert_file = None 153 | """client certificate file 154 | """ 155 | self.key_file = None 156 | """client key file 157 | """ 158 | self.assert_hostname = None 159 | """Set this to True/False to enable/disable SSL hostname verification. 160 | """ 161 | self.tls_server_name = None 162 | """SSL/TLS Server Name Indication (SNI) 163 | Set this to the SNI value expected by the server. 164 | """ 165 | 166 | self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 167 | """urllib3 connection pool's maximum number of connections saved 168 | per pool. urllib3 uses 1 connection as default value, but this is 169 | not the best value when you are making a lot of possibly parallel 170 | requests to the same host, which is often the case here. 171 | cpu_count * 5 is used as default value to increase performance. 172 | """ 173 | 174 | self.proxy: Optional[str] = None 175 | """Proxy URL 176 | """ 177 | self.proxy_headers = None 178 | """Proxy headers 179 | """ 180 | self.safe_chars_for_path_param = '' 181 | """Safe chars for path_param 182 | """ 183 | self.retries = retries 184 | """Adding retries to override urllib3 default value 3 185 | """ 186 | # Enable client side validation 187 | self.client_side_validation = True 188 | 189 | self.socket_options = None 190 | """Options to pass down to the underlying urllib3 socket 191 | """ 192 | 193 | self.datetime_format = "%Y-%m-%dT%H:%M:%S.%f%z" 194 | """datetime format 195 | """ 196 | 197 | self.date_format = "%Y-%m-%d" 198 | """date format 199 | """ 200 | 201 | def __deepcopy__(self, memo): 202 | cls = self.__class__ 203 | result = cls.__new__(cls) 204 | memo[id(self)] = result 205 | for k, v in self.__dict__.items(): 206 | if k not in ('logger', 'logger_file_handler'): 207 | setattr(result, k, copy.deepcopy(v, memo)) 208 | # shallow copy of loggers 209 | result.logger = copy.copy(self.logger) 210 | # use setters to configure loggers 211 | result.logger_file = self.logger_file 212 | result.debug = self.debug 213 | return result 214 | 215 | def __setattr__(self, name, value): 216 | object.__setattr__(self, name, value) 217 | 218 | @classmethod 219 | def set_default(cls, default): 220 | """Set default instance of configuration. 221 | 222 | It stores default configuration, which can be 223 | returned by get_default_copy method. 224 | 225 | :param default: object of Configuration 226 | """ 227 | cls._default = default 228 | 229 | @classmethod 230 | def get_default_copy(cls): 231 | """Deprecated. Please use `get_default` instead. 232 | 233 | Deprecated. Please use `get_default` instead. 234 | 235 | :return: The configuration object. 236 | """ 237 | return cls.get_default() 238 | 239 | @classmethod 240 | def get_default(cls): 241 | """Return the default configuration. 242 | 243 | This method returns newly created, based on default constructor, 244 | object of Configuration class or returns a copy of default 245 | configuration. 246 | 247 | :return: The configuration object. 248 | """ 249 | if cls._default is None: 250 | cls._default = Configuration() 251 | return cls._default 252 | 253 | @property 254 | def logger_file(self): 255 | """The logger file. 256 | 257 | If the logger_file is None, then add stream handler and remove file 258 | handler. Otherwise, add file handler and remove stream handler. 259 | 260 | :param value: The logger_file path. 261 | :type: str 262 | """ 263 | return self.__logger_file 264 | 265 | @logger_file.setter 266 | def logger_file(self, value): 267 | """The logger file. 268 | 269 | If the logger_file is None, then add stream handler and remove file 270 | handler. Otherwise, add file handler and remove stream handler. 271 | 272 | :param value: The logger_file path. 273 | :type: str 274 | """ 275 | self.__logger_file = value 276 | if self.__logger_file: 277 | # If set logging file, 278 | # then add file handler and remove stream handler. 279 | self.logger_file_handler = logging.FileHandler(self.__logger_file) 280 | self.logger_file_handler.setFormatter(self.logger_formatter) 281 | for _, logger in self.logger.items(): 282 | logger.addHandler(self.logger_file_handler) 283 | 284 | @property 285 | def debug(self): 286 | """Debug status 287 | 288 | :param value: The debug status, True or False. 289 | :type: bool 290 | """ 291 | return self.__debug 292 | 293 | @debug.setter 294 | def debug(self, value): 295 | """Debug status 296 | 297 | :param value: The debug status, True or False. 298 | :type: bool 299 | """ 300 | self.__debug = value 301 | if self.__debug: 302 | # if debug status is True, turn on debug logging 303 | for _, logger in self.logger.items(): 304 | logger.setLevel(logging.DEBUG) 305 | # turn on httplib debug 306 | httplib.HTTPConnection.debuglevel = 1 307 | else: 308 | # if debug status is False, turn off debug logging, 309 | # setting log level to default `logging.WARNING` 310 | for _, logger in self.logger.items(): 311 | logger.setLevel(logging.WARNING) 312 | # turn off httplib debug 313 | httplib.HTTPConnection.debuglevel = 0 314 | 315 | @property 316 | def logger_format(self): 317 | """The logger format. 318 | 319 | The logger_formatter will be updated when sets logger_format. 320 | 321 | :param value: The format string. 322 | :type: str 323 | """ 324 | return self.__logger_format 325 | 326 | @logger_format.setter 327 | def logger_format(self, value): 328 | """The logger format. 329 | 330 | The logger_formatter will be updated when sets logger_format. 331 | 332 | :param value: The format string. 333 | :type: str 334 | """ 335 | self.__logger_format = value 336 | self.logger_formatter = logging.Formatter(self.__logger_format) 337 | 338 | def get_api_key_with_prefix(self, identifier, alias=None): 339 | """Gets API key (with prefix if set). 340 | 341 | :param identifier: The identifier of apiKey. 342 | :param alias: The alternative identifier of apiKey. 343 | :return: The token for api key authentication. 344 | """ 345 | if self.refresh_api_key_hook is not None: 346 | self.refresh_api_key_hook(self) 347 | key = self.api_key.get(identifier, self.api_key.get(alias) if alias is not None else None) 348 | if key: 349 | prefix = self.api_key_prefix.get(identifier) 350 | if prefix: 351 | return "%s %s" % (prefix, key) 352 | else: 353 | return key 354 | 355 | def get_basic_auth_token(self): 356 | """Gets HTTP basic authentication header (string). 357 | 358 | :return: The token for basic HTTP authentication. 359 | """ 360 | username = "" 361 | if self.username is not None: 362 | username = self.username 363 | password = "" 364 | if self.password is not None: 365 | password = self.password 366 | return urllib3.util.make_headers( 367 | basic_auth=username + ':' + password 368 | ).get('authorization') 369 | 370 | def auth_settings(self): 371 | """Gets Auth Settings dict for api client. 372 | 373 | :return: The Auth Settings information dict. 374 | """ 375 | auth = {} 376 | return auth 377 | 378 | def to_debug_report(self): 379 | """Gets the essential information for debugging. 380 | 381 | :return: The report for debugging. 382 | """ 383 | return "Python SDK Debug Report:\n"\ 384 | "OS: {env}\n"\ 385 | "Python Version: {pyversion}\n"\ 386 | "Version of the API: 1.0.0\n"\ 387 | "SDK Package Version: 1.0.0".\ 388 | format(env=sys.platform, pyversion=sys.version) 389 | 390 | def get_host_settings(self): 391 | """Gets an array of host settings 392 | 393 | :return: An array of host settings 394 | """ 395 | return [ 396 | { 397 | 'url': "", 398 | 'description': "No description provided", 399 | } 400 | ] 401 | 402 | def get_host_from_settings(self, index, variables=None, servers=None): 403 | """Gets host URL based on the index and variables 404 | :param index: array index of the host settings 405 | :param variables: hash of variable and the corresponding value 406 | :param servers: an array of host settings or None 407 | :return: URL based on host settings 408 | """ 409 | if index is None: 410 | return self._base_path 411 | 412 | variables = {} if variables is None else variables 413 | servers = self.get_host_settings() if servers is None else servers 414 | 415 | try: 416 | server = servers[index] 417 | except IndexError: 418 | raise ValueError( 419 | "Invalid index {0} when selecting the host settings. " 420 | "Must be less than {1}".format(index, len(servers))) 421 | 422 | url = server['url'] 423 | 424 | # go through variables and replace placeholders 425 | for variable_name, variable in server.get('variables', {}).items(): 426 | used_value = variables.get( 427 | variable_name, variable['default_value']) 428 | 429 | if 'enum_values' in variable \ 430 | and used_value not in variable['enum_values']: 431 | raise ValueError( 432 | "The variable `{0}` in the host URL has invalid value " 433 | "{1}. Must be {2}.".format( 434 | variable_name, variables[variable_name], 435 | variable['enum_values'])) 436 | 437 | url = url.replace("{" + variable_name + "}", used_value) 438 | 439 | return url 440 | 441 | @property 442 | def host(self): 443 | """Return generated host.""" 444 | return self.get_host_from_settings(self.server_index, variables=self.server_variables) 445 | 446 | @host.setter 447 | def host(self, value): 448 | """Fix base path.""" 449 | self._base_path = value 450 | self.server_index = None 451 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/exceptions.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | from typing import Any, Optional 15 | from typing_extensions import Self 16 | 17 | class OpenApiException(Exception): 18 | """The base exception class for all OpenAPIExceptions""" 19 | 20 | 21 | class ApiTypeError(OpenApiException, TypeError): 22 | def __init__(self, msg, path_to_item=None, valid_classes=None, 23 | key_type=None) -> None: 24 | """ Raises an exception for TypeErrors 25 | 26 | Args: 27 | msg (str): the exception message 28 | 29 | Keyword Args: 30 | path_to_item (list): a list of keys an indices to get to the 31 | current_item 32 | None if unset 33 | valid_classes (tuple): the primitive classes that current item 34 | should be an instance of 35 | None if unset 36 | key_type (bool): False if our value is a value in a dict 37 | True if it is a key in a dict 38 | False if our item is an item in a list 39 | None if unset 40 | """ 41 | self.path_to_item = path_to_item 42 | self.valid_classes = valid_classes 43 | self.key_type = key_type 44 | full_msg = msg 45 | if path_to_item: 46 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 47 | super(ApiTypeError, self).__init__(full_msg) 48 | 49 | 50 | class ApiValueError(OpenApiException, ValueError): 51 | def __init__(self, msg, path_to_item=None) -> None: 52 | """ 53 | Args: 54 | msg (str): the exception message 55 | 56 | Keyword Args: 57 | path_to_item (list) the path to the exception in the 58 | received_data dict. None if unset 59 | """ 60 | 61 | self.path_to_item = path_to_item 62 | full_msg = msg 63 | if path_to_item: 64 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 65 | super(ApiValueError, self).__init__(full_msg) 66 | 67 | 68 | class ApiAttributeError(OpenApiException, AttributeError): 69 | def __init__(self, msg, path_to_item=None) -> None: 70 | """ 71 | Raised when an attribute reference or assignment fails. 72 | 73 | Args: 74 | msg (str): the exception message 75 | 76 | Keyword Args: 77 | path_to_item (None/list) the path to the exception in the 78 | received_data dict 79 | """ 80 | self.path_to_item = path_to_item 81 | full_msg = msg 82 | if path_to_item: 83 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 84 | super(ApiAttributeError, self).__init__(full_msg) 85 | 86 | 87 | class ApiKeyError(OpenApiException, KeyError): 88 | def __init__(self, msg, path_to_item=None) -> None: 89 | """ 90 | Args: 91 | msg (str): the exception message 92 | 93 | Keyword Args: 94 | path_to_item (None/list) the path to the exception in the 95 | received_data dict 96 | """ 97 | self.path_to_item = path_to_item 98 | full_msg = msg 99 | if path_to_item: 100 | full_msg = "{0} at {1}".format(msg, render_path(path_to_item)) 101 | super(ApiKeyError, self).__init__(full_msg) 102 | 103 | 104 | class ApiException(OpenApiException): 105 | 106 | def __init__( 107 | self, 108 | status=None, 109 | reason=None, 110 | http_resp=None, 111 | *, 112 | body: Optional[str] = None, 113 | data: Optional[Any] = None, 114 | ) -> None: 115 | self.status = status 116 | self.reason = reason 117 | self.body = body 118 | self.data = data 119 | self.headers = None 120 | 121 | if http_resp: 122 | if self.status is None: 123 | self.status = http_resp.status 124 | if self.reason is None: 125 | self.reason = http_resp.reason 126 | if self.body is None: 127 | try: 128 | self.body = http_resp.data.decode('utf-8') 129 | except Exception: 130 | pass 131 | self.headers = http_resp.getheaders() 132 | 133 | @classmethod 134 | def from_response( 135 | cls, 136 | *, 137 | http_resp, 138 | body: Optional[str], 139 | data: Optional[Any], 140 | ) -> Self: 141 | if http_resp.status == 400: 142 | raise BadRequestException(http_resp=http_resp, body=body, data=data) 143 | 144 | if http_resp.status == 401: 145 | raise UnauthorizedException(http_resp=http_resp, body=body, data=data) 146 | 147 | if http_resp.status == 403: 148 | raise ForbiddenException(http_resp=http_resp, body=body, data=data) 149 | 150 | if http_resp.status == 404: 151 | raise NotFoundException(http_resp=http_resp, body=body, data=data) 152 | 153 | if 500 <= http_resp.status <= 599: 154 | raise ServiceException(http_resp=http_resp, body=body, data=data) 155 | raise ApiException(http_resp=http_resp, body=body, data=data) 156 | 157 | def __str__(self): 158 | """Custom error messages for exception""" 159 | error_message = "({0})\n"\ 160 | "Reason: {1}\n".format(self.status, self.reason) 161 | if self.headers: 162 | error_message += "HTTP response headers: {0}\n".format( 163 | self.headers) 164 | 165 | if self.data or self.body: 166 | error_message += "HTTP response body: {0}\n".format(self.data or self.body) 167 | 168 | return error_message 169 | 170 | 171 | class BadRequestException(ApiException): 172 | pass 173 | 174 | 175 | class NotFoundException(ApiException): 176 | pass 177 | 178 | 179 | class UnauthorizedException(ApiException): 180 | pass 181 | 182 | 183 | class ForbiddenException(ApiException): 184 | pass 185 | 186 | 187 | class ServiceException(ApiException): 188 | pass 189 | 190 | 191 | def render_path(path_to_item): 192 | """Returns a string representation of a path""" 193 | result = "" 194 | for pth in path_to_item: 195 | if isinstance(pth, int): 196 | result += "[{0}]".format(pth) 197 | else: 198 | result += "['{0}']".format(pth) 199 | return result 200 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/models/__init__.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | # flake8: noqa 4 | """ 5 | Open Message Format 6 | 7 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 8 | 9 | The version of the OpenAPI document: 1.0.0 10 | Generated by OpenAPI Generator (https://openapi-generator.tech) 11 | 12 | Do not edit the class manually. 13 | """ # noqa: E501 14 | 15 | 16 | # import models into model package 17 | from openapi_client.models.content_item import ContentItem 18 | from openapi_client.models.message import Message 19 | from openapi_client.models.message_content import MessageContent 20 | from openapi_client.models.metadata import Metadata 21 | from openapi_client.models.response_message import ResponseMessage 22 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/models/content_item.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | from __future__ import annotations 16 | import pprint 17 | import re # noqa: F401 18 | import json 19 | 20 | from pydantic import BaseModel, ConfigDict, Field, StrictStr 21 | from typing import Any, ClassVar, Dict, List, Optional 22 | from typing import Optional, Set 23 | from typing_extensions import Self 24 | 25 | class ContentItem(BaseModel): 26 | """ 27 | ContentItem 28 | """ # noqa: E501 29 | type: StrictStr = Field(description="The type of content, such as text or image URL.") 30 | text: Optional[StrictStr] = Field(default=None, description="The text content. Required if type is 'text'.") 31 | image_url: Optional[object] = Field(default=None, description="The image URL object. Required if type is 'image_url'.") 32 | source: Optional[object] = Field(default=None, description="The source object. Required if type is 'image'.") 33 | __properties: ClassVar[List[str]] = [] 34 | 35 | model_config = ConfigDict( 36 | populate_by_name=True, 37 | validate_assignment=True, 38 | protected_namespaces=(), 39 | ) 40 | 41 | 42 | def to_str(self) -> str: 43 | """Returns the string representation of the model using alias""" 44 | return pprint.pformat(self.model_dump(by_alias=True)) 45 | 46 | def to_json(self) -> str: 47 | """Returns the JSON representation of the model using alias""" 48 | # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead 49 | return json.dumps(self.to_dict()) 50 | 51 | @classmethod 52 | def from_json(cls, json_str: str) -> Optional[Self]: 53 | """Create an instance of ContentItem from a JSON string""" 54 | return cls.from_dict(json.loads(json_str)) 55 | 56 | def to_dict(self) -> Dict[str, Any]: 57 | """Return the dictionary representation of the model using alias. 58 | 59 | This has the following differences from calling pydantic's 60 | `self.model_dump(by_alias=True)`: 61 | 62 | * `None` is only added to the output dict for nullable fields that 63 | were set at model initialization. Other fields with value `None` 64 | are ignored. 65 | """ 66 | excluded_fields: Set[str] = set([ 67 | ]) 68 | 69 | _dict = self.model_dump( 70 | by_alias=True, 71 | exclude=excluded_fields, 72 | exclude_none=True, 73 | ) 74 | return _dict 75 | 76 | @classmethod 77 | def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: 78 | """Create an instance of ContentItem from a dict""" 79 | if obj is None: 80 | return None 81 | 82 | if not isinstance(obj, dict): 83 | return cls.model_validate(obj) 84 | 85 | _obj = cls.model_validate({ 86 | }) 87 | return _obj 88 | 89 | 90 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/models/message.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | from __future__ import annotations 16 | import pprint 17 | import re # noqa: F401 18 | import json 19 | 20 | from pydantic import BaseModel, ConfigDict, Field, StrictStr 21 | from typing import Any, ClassVar, Dict, List 22 | from openapi_client.models.message_content import MessageContent 23 | from typing import Optional, Set 24 | from typing_extensions import Self 25 | 26 | class Message(BaseModel): 27 | """ 28 | Message 29 | """ # noqa: E501 30 | role: StrictStr = Field(description="Role of the message sender. Examples include: - `system`: For system messages - `user`: For messages from the user - `assistant`: For messages from the assistant ") 31 | content: MessageContent 32 | __properties: ClassVar[List[str]] = ["role", "content"] 33 | 34 | model_config = ConfigDict( 35 | populate_by_name=True, 36 | validate_assignment=True, 37 | protected_namespaces=(), 38 | ) 39 | 40 | 41 | def to_str(self) -> str: 42 | """Returns the string representation of the model using alias""" 43 | return pprint.pformat(self.model_dump(by_alias=True)) 44 | 45 | def to_json(self) -> str: 46 | """Returns the JSON representation of the model using alias""" 47 | # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead 48 | return json.dumps(self.to_dict()) 49 | 50 | @classmethod 51 | def from_json(cls, json_str: str) -> Optional[Self]: 52 | """Create an instance of Message from a JSON string""" 53 | return cls.from_dict(json.loads(json_str)) 54 | 55 | def to_dict(self) -> Dict[str, Any]: 56 | """Return the dictionary representation of the model using alias. 57 | 58 | This has the following differences from calling pydantic's 59 | `self.model_dump(by_alias=True)`: 60 | 61 | * `None` is only added to the output dict for nullable fields that 62 | were set at model initialization. Other fields with value `None` 63 | are ignored. 64 | """ 65 | excluded_fields: Set[str] = set([ 66 | ]) 67 | 68 | _dict = self.model_dump( 69 | by_alias=True, 70 | exclude=excluded_fields, 71 | exclude_none=True, 72 | ) 73 | # override the default output from pydantic by calling `to_dict()` of content 74 | if self.content: 75 | _dict['content'] = self.content.to_dict() 76 | return _dict 77 | 78 | @classmethod 79 | def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: 80 | """Create an instance of Message from a dict""" 81 | if obj is None: 82 | return None 83 | 84 | if not isinstance(obj, dict): 85 | return cls.model_validate(obj) 86 | 87 | _obj = cls.model_validate({ 88 | "role": obj.get("role"), 89 | "content": MessageContent.from_dict(obj["content"]) if obj.get("content") is not None else None 90 | }) 91 | return _obj 92 | 93 | 94 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/models/message_content.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | from __future__ import annotations 16 | import json 17 | import pprint 18 | from pydantic import BaseModel, ConfigDict, Field, StrictStr, ValidationError, field_validator 19 | from typing import Any, List, Optional 20 | from openapi_client.models.content_item import ContentItem 21 | from pydantic import StrictStr, Field 22 | from typing import Union, List, Set, Optional, Dict 23 | from typing_extensions import Literal, Self 24 | 25 | MESSAGECONTENT_ONE_OF_SCHEMAS = ["List[ContentItem]", "str"] 26 | 27 | class MessageContent(BaseModel): 28 | """ 29 | MessageContent 30 | """ 31 | # data type: List[ContentItem] 32 | oneof_schema_1_validator: Optional[List[Optional[ContentItem]]] = Field(default=None, description="Content of the message. It can be a list of content items, such as text and images. ") 33 | # data type: str 34 | oneof_schema_2_validator: Optional[StrictStr] = Field(default=None, description="text content of the message,") 35 | actual_instance: Optional[Union[List[ContentItem], str]] = None 36 | one_of_schemas: Set[str] = { "List[ContentItem]", "str" } 37 | 38 | model_config = ConfigDict( 39 | validate_assignment=True, 40 | protected_namespaces=(), 41 | ) 42 | 43 | 44 | def __init__(self, *args, **kwargs) -> None: 45 | if args: 46 | if len(args) > 1: 47 | raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") 48 | if kwargs: 49 | raise ValueError("If a position argument is used, keyword arguments cannot be used.") 50 | super().__init__(actual_instance=args[0]) 51 | else: 52 | super().__init__(**kwargs) 53 | 54 | @field_validator('actual_instance') 55 | def actual_instance_must_validate_oneof(cls, v): 56 | instance = MessageContent.model_construct() 57 | error_messages = [] 58 | match = 0 59 | # validate data type: List[ContentItem] 60 | try: 61 | instance.oneof_schema_1_validator = v 62 | match += 1 63 | except (ValidationError, ValueError) as e: 64 | error_messages.append(str(e)) 65 | # validate data type: str 66 | try: 67 | instance.oneof_schema_2_validator = v 68 | match += 1 69 | except (ValidationError, ValueError) as e: 70 | error_messages.append(str(e)) 71 | if match > 1: 72 | # more than 1 match 73 | raise ValueError("Multiple matches found when setting `actual_instance` in MessageContent with oneOf schemas: List[ContentItem], str. Details: " + ", ".join(error_messages)) 74 | elif match == 0: 75 | # no match 76 | raise ValueError("No match found when setting `actual_instance` in MessageContent with oneOf schemas: List[ContentItem], str. Details: " + ", ".join(error_messages)) 77 | else: 78 | return v 79 | 80 | @classmethod 81 | def from_dict(cls, obj: Union[str, Dict[str, Any]]) -> Self: 82 | return cls.from_json(json.dumps(obj)) 83 | 84 | @classmethod 85 | def from_json(cls, json_str: str) -> Self: 86 | """Returns the object represented by the json string""" 87 | instance = cls.model_construct() 88 | error_messages = [] 89 | match = 0 90 | 91 | # deserialize data into List[ContentItem] 92 | try: 93 | # validation 94 | instance.oneof_schema_1_validator = json.loads(json_str) 95 | # assign value to actual_instance 96 | instance.actual_instance = instance.oneof_schema_1_validator 97 | match += 1 98 | except (ValidationError, ValueError) as e: 99 | error_messages.append(str(e)) 100 | # deserialize data into str 101 | try: 102 | # validation 103 | instance.oneof_schema_2_validator = json.loads(json_str) 104 | # assign value to actual_instance 105 | instance.actual_instance = instance.oneof_schema_2_validator 106 | match += 1 107 | except (ValidationError, ValueError) as e: 108 | error_messages.append(str(e)) 109 | 110 | if match > 1: 111 | # more than 1 match 112 | raise ValueError("Multiple matches found when deserializing the JSON string into MessageContent with oneOf schemas: List[ContentItem], str. Details: " + ", ".join(error_messages)) 113 | elif match == 0: 114 | # no match 115 | raise ValueError("No match found when deserializing the JSON string into MessageContent with oneOf schemas: List[ContentItem], str. Details: " + ", ".join(error_messages)) 116 | else: 117 | return instance 118 | 119 | def to_json(self) -> str: 120 | """Returns the JSON representation of the actual instance""" 121 | if self.actual_instance is None: 122 | return "null" 123 | 124 | if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): 125 | return self.actual_instance.to_json() 126 | else: 127 | return json.dumps(self.actual_instance) 128 | 129 | def to_dict(self) -> Optional[Union[Dict[str, Any], List[ContentItem], str]]: 130 | """Returns the dict representation of the actual instance""" 131 | if self.actual_instance is None: 132 | return None 133 | 134 | if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): 135 | return self.actual_instance.to_dict() 136 | else: 137 | # primitive type 138 | return self.actual_instance 139 | 140 | def to_str(self) -> str: 141 | """Returns the string representation of the actual instance""" 142 | return pprint.pformat(self.model_dump()) 143 | 144 | 145 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/models/metadata.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | from __future__ import annotations 16 | import pprint 17 | import re # noqa: F401 18 | import json 19 | 20 | from pydantic import BaseModel, ConfigDict, Field, StrictStr 21 | from typing import Any, ClassVar, Dict, List, Optional 22 | from typing import Optional, Set 23 | from typing_extensions import Self 24 | 25 | class Metadata(BaseModel): 26 | """ 27 | Metadata 28 | """ # noqa: E501 29 | model: StrictStr = Field(description="The name of the model used to generate the response.") 30 | tokens: Optional[StrictStr] = Field(default=None, description="The number of tokens used to generate the response.") 31 | __properties: ClassVar[List[str]] = ["model", "tokens"] 32 | 33 | model_config = ConfigDict( 34 | populate_by_name=True, 35 | validate_assignment=True, 36 | protected_namespaces=(), 37 | ) 38 | 39 | 40 | def to_str(self) -> str: 41 | """Returns the string representation of the model using alias""" 42 | return pprint.pformat(self.model_dump(by_alias=True)) 43 | 44 | def to_json(self) -> str: 45 | """Returns the JSON representation of the model using alias""" 46 | # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead 47 | return json.dumps(self.to_dict()) 48 | 49 | @classmethod 50 | def from_json(cls, json_str: str) -> Optional[Self]: 51 | """Create an instance of Metadata from a JSON string""" 52 | return cls.from_dict(json.loads(json_str)) 53 | 54 | def to_dict(self) -> Dict[str, Any]: 55 | """Return the dictionary representation of the model using alias. 56 | 57 | This has the following differences from calling pydantic's 58 | `self.model_dump(by_alias=True)`: 59 | 60 | * `None` is only added to the output dict for nullable fields that 61 | were set at model initialization. Other fields with value `None` 62 | are ignored. 63 | """ 64 | excluded_fields: Set[str] = set([ 65 | ]) 66 | 67 | _dict = self.model_dump( 68 | by_alias=True, 69 | exclude=excluded_fields, 70 | exclude_none=True, 71 | ) 72 | return _dict 73 | 74 | @classmethod 75 | def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: 76 | """Create an instance of Metadata from a dict""" 77 | if obj is None: 78 | return None 79 | 80 | if not isinstance(obj, dict): 81 | return cls.model_validate(obj) 82 | 83 | _obj = cls.model_validate({ 84 | "model": obj.get("model"), 85 | "tokens": obj.get("tokens") 86 | }) 87 | return _obj 88 | 89 | 90 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/models/response_message.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | from __future__ import annotations 16 | import pprint 17 | import re # noqa: F401 18 | import json 19 | 20 | from pydantic import BaseModel, ConfigDict, Field, StrictStr 21 | from typing import Any, ClassVar, Dict, List, Optional 22 | from openapi_client.models.metadata import Metadata 23 | from typing import Optional, Set 24 | from typing_extensions import Self 25 | 26 | class ResponseMessage(BaseModel): 27 | """ 28 | ResponseMessage 29 | """ # noqa: E501 30 | content: StrictStr = Field(description="The content of the response message.") 31 | refusal: Optional[StrictStr] = Field(default=None, description="The refusal message if applicable, or null if not.") 32 | role: StrictStr = Field(description="The role of the author of the message (e.g., 'assistant').") 33 | metadata: Optional[Metadata] = None 34 | __properties: ClassVar[List[str]] = ["content", "refusal", "role", "metadata"] 35 | 36 | model_config = ConfigDict( 37 | populate_by_name=True, 38 | validate_assignment=True, 39 | protected_namespaces=(), 40 | ) 41 | 42 | 43 | def to_str(self) -> str: 44 | """Returns the string representation of the model using alias""" 45 | return pprint.pformat(self.model_dump(by_alias=True)) 46 | 47 | def to_json(self) -> str: 48 | """Returns the JSON representation of the model using alias""" 49 | # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead 50 | return json.dumps(self.to_dict()) 51 | 52 | @classmethod 53 | def from_json(cls, json_str: str) -> Optional[Self]: 54 | """Create an instance of ResponseMessage from a JSON string""" 55 | return cls.from_dict(json.loads(json_str)) 56 | 57 | def to_dict(self) -> Dict[str, Any]: 58 | """Return the dictionary representation of the model using alias. 59 | 60 | This has the following differences from calling pydantic's 61 | `self.model_dump(by_alias=True)`: 62 | 63 | * `None` is only added to the output dict for nullable fields that 64 | were set at model initialization. Other fields with value `None` 65 | are ignored. 66 | """ 67 | excluded_fields: Set[str] = set([ 68 | ]) 69 | 70 | _dict = self.model_dump( 71 | by_alias=True, 72 | exclude=excluded_fields, 73 | exclude_none=True, 74 | ) 75 | # override the default output from pydantic by calling `to_dict()` of metadata 76 | if self.metadata: 77 | _dict['metadata'] = self.metadata.to_dict() 78 | # set to None if refusal (nullable) is None 79 | # and model_fields_set contains the field 80 | if self.refusal is None and "refusal" in self.model_fields_set: 81 | _dict['refusal'] = None 82 | 83 | return _dict 84 | 85 | @classmethod 86 | def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]: 87 | """Create an instance of ResponseMessage from a dict""" 88 | if obj is None: 89 | return None 90 | 91 | if not isinstance(obj, dict): 92 | return cls.model_validate(obj) 93 | 94 | _obj = cls.model_validate({ 95 | "content": obj.get("content"), 96 | "refusal": obj.get("refusal"), 97 | "role": obj.get("role"), 98 | "metadata": Metadata.from_dict(obj["metadata"]) if obj.get("metadata") is not None else None 99 | }) 100 | return _obj 101 | 102 | 103 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-llm-initiative/open-message-format/c3382c576ede746fc14e1b600a5ed084104bbbae/examples/clients/example_python_client/openapi_client/py.typed -------------------------------------------------------------------------------- /examples/clients/example_python_client/openapi_client/rest.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import io 16 | import json 17 | import re 18 | import ssl 19 | 20 | import urllib3 21 | 22 | from openapi_client.exceptions import ApiException, ApiValueError 23 | 24 | SUPPORTED_SOCKS_PROXIES = {"socks5", "socks5h", "socks4", "socks4a"} 25 | RESTResponseType = urllib3.HTTPResponse 26 | 27 | 28 | def is_socks_proxy_url(url): 29 | if url is None: 30 | return False 31 | split_section = url.split("://") 32 | if len(split_section) < 2: 33 | return False 34 | else: 35 | return split_section[0].lower() in SUPPORTED_SOCKS_PROXIES 36 | 37 | 38 | class RESTResponse(io.IOBase): 39 | 40 | def __init__(self, resp) -> None: 41 | self.response = resp 42 | self.status = resp.status 43 | self.reason = resp.reason 44 | self.data = None 45 | 46 | def read(self): 47 | if self.data is None: 48 | self.data = self.response.data 49 | return self.data 50 | 51 | def getheaders(self): 52 | """Returns a dictionary of the response headers.""" 53 | return self.response.headers 54 | 55 | def getheader(self, name, default=None): 56 | """Returns a given response header.""" 57 | return self.response.headers.get(name, default) 58 | 59 | 60 | class RESTClientObject: 61 | 62 | def __init__(self, configuration) -> None: 63 | # urllib3.PoolManager will pass all kw parameters to connectionpool 64 | # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 65 | # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 66 | # Custom SSL certificates and client certificates: http://urllib3.readthedocs.io/en/latest/advanced-usage.html # noqa: E501 67 | 68 | # cert_reqs 69 | if configuration.verify_ssl: 70 | cert_reqs = ssl.CERT_REQUIRED 71 | else: 72 | cert_reqs = ssl.CERT_NONE 73 | 74 | pool_args = { 75 | "cert_reqs": cert_reqs, 76 | "ca_certs": configuration.ssl_ca_cert, 77 | "cert_file": configuration.cert_file, 78 | "key_file": configuration.key_file, 79 | } 80 | if configuration.assert_hostname is not None: 81 | pool_args['assert_hostname'] = ( 82 | configuration.assert_hostname 83 | ) 84 | 85 | if configuration.retries is not None: 86 | pool_args['retries'] = configuration.retries 87 | 88 | if configuration.tls_server_name: 89 | pool_args['server_hostname'] = configuration.tls_server_name 90 | 91 | 92 | if configuration.socket_options is not None: 93 | pool_args['socket_options'] = configuration.socket_options 94 | 95 | if configuration.connection_pool_maxsize is not None: 96 | pool_args['maxsize'] = configuration.connection_pool_maxsize 97 | 98 | # https pool manager 99 | self.pool_manager: urllib3.PoolManager 100 | 101 | if configuration.proxy: 102 | if is_socks_proxy_url(configuration.proxy): 103 | from urllib3.contrib.socks import SOCKSProxyManager 104 | pool_args["proxy_url"] = configuration.proxy 105 | pool_args["headers"] = configuration.proxy_headers 106 | self.pool_manager = SOCKSProxyManager(**pool_args) 107 | else: 108 | pool_args["proxy_url"] = configuration.proxy 109 | pool_args["proxy_headers"] = configuration.proxy_headers 110 | self.pool_manager = urllib3.ProxyManager(**pool_args) 111 | else: 112 | self.pool_manager = urllib3.PoolManager(**pool_args) 113 | 114 | def request( 115 | self, 116 | method, 117 | url, 118 | headers=None, 119 | body=None, 120 | post_params=None, 121 | _request_timeout=None 122 | ): 123 | """Perform requests. 124 | 125 | :param method: http request method 126 | :param url: http request url 127 | :param headers: http request headers 128 | :param body: request json body, for `application/json` 129 | :param post_params: request post parameters, 130 | `application/x-www-form-urlencoded` 131 | and `multipart/form-data` 132 | :param _request_timeout: timeout setting for this request. If one 133 | number provided, it will be total request 134 | timeout. It can also be a pair (tuple) of 135 | (connection, read) timeouts. 136 | """ 137 | method = method.upper() 138 | assert method in [ 139 | 'GET', 140 | 'HEAD', 141 | 'DELETE', 142 | 'POST', 143 | 'PUT', 144 | 'PATCH', 145 | 'OPTIONS' 146 | ] 147 | 148 | if post_params and body: 149 | raise ApiValueError( 150 | "body parameter cannot be used with post_params parameter." 151 | ) 152 | 153 | post_params = post_params or {} 154 | headers = headers or {} 155 | 156 | timeout = None 157 | if _request_timeout: 158 | if isinstance(_request_timeout, (int, float)): 159 | timeout = urllib3.Timeout(total=_request_timeout) 160 | elif ( 161 | isinstance(_request_timeout, tuple) 162 | and len(_request_timeout) == 2 163 | ): 164 | timeout = urllib3.Timeout( 165 | connect=_request_timeout[0], 166 | read=_request_timeout[1] 167 | ) 168 | 169 | try: 170 | # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` 171 | if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: 172 | 173 | # no content type provided or payload is json 174 | content_type = headers.get('Content-Type') 175 | if ( 176 | not content_type 177 | or re.search('json', content_type, re.IGNORECASE) 178 | ): 179 | request_body = None 180 | if body is not None: 181 | request_body = json.dumps(body) 182 | r = self.pool_manager.request( 183 | method, 184 | url, 185 | body=request_body, 186 | timeout=timeout, 187 | headers=headers, 188 | preload_content=False 189 | ) 190 | elif content_type == 'application/x-www-form-urlencoded': 191 | r = self.pool_manager.request( 192 | method, 193 | url, 194 | fields=post_params, 195 | encode_multipart=False, 196 | timeout=timeout, 197 | headers=headers, 198 | preload_content=False 199 | ) 200 | elif content_type == 'multipart/form-data': 201 | # must del headers['Content-Type'], or the correct 202 | # Content-Type which generated by urllib3 will be 203 | # overwritten. 204 | del headers['Content-Type'] 205 | # Ensures that dict objects are serialized 206 | post_params = [(a, json.dumps(b)) if isinstance(b, dict) else (a,b) for a, b in post_params] 207 | r = self.pool_manager.request( 208 | method, 209 | url, 210 | fields=post_params, 211 | encode_multipart=True, 212 | timeout=timeout, 213 | headers=headers, 214 | preload_content=False 215 | ) 216 | # Pass a `string` parameter directly in the body to support 217 | # other content types than JSON when `body` argument is 218 | # provided in serialized form. 219 | elif isinstance(body, str) or isinstance(body, bytes): 220 | r = self.pool_manager.request( 221 | method, 222 | url, 223 | body=body, 224 | timeout=timeout, 225 | headers=headers, 226 | preload_content=False 227 | ) 228 | elif headers['Content-Type'] == 'text/plain' and isinstance(body, bool): 229 | request_body = "true" if body else "false" 230 | r = self.pool_manager.request( 231 | method, 232 | url, 233 | body=request_body, 234 | preload_content=False, 235 | timeout=timeout, 236 | headers=headers) 237 | else: 238 | # Cannot generate the request from given parameters 239 | msg = """Cannot prepare a request message for provided 240 | arguments. Please check that your arguments match 241 | declared content type.""" 242 | raise ApiException(status=0, reason=msg) 243 | # For `GET`, `HEAD` 244 | else: 245 | r = self.pool_manager.request( 246 | method, 247 | url, 248 | fields={}, 249 | timeout=timeout, 250 | headers=headers, 251 | preload_content=False 252 | ) 253 | except urllib3.exceptions.SSLError as e: 254 | msg = "\n".join([type(e).__name__, str(e)]) 255 | raise ApiException(status=0, reason=msg) 256 | 257 | return RESTResponse(r) 258 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "openapi_client" 3 | version = "1.0.0" 4 | description = "Open Message Format" 5 | authors = ["OpenAPI Generator Community "] 6 | license = "NoLicense" 7 | readme = "README.md" 8 | repository = "https://github.com/GIT_USER_ID/GIT_REPO_ID" 9 | keywords = ["OpenAPI", "OpenAPI-Generator", "Open Message Format"] 10 | include = ["openapi_client/py.typed"] 11 | 12 | [tool.poetry.dependencies] 13 | python = "^3.7" 14 | 15 | urllib3 = ">= 1.25.3" 16 | python-dateutil = ">=2.8.2" 17 | pydantic = ">=2" 18 | typing-extensions = ">=4.7.1" 19 | 20 | [tool.poetry.dev-dependencies] 21 | pytest = ">=7.2.1" 22 | tox = ">=3.9.0" 23 | flake8 = ">=4.0.0" 24 | types-python-dateutil = ">=2.8.19.14" 25 | mypy = "1.4.1" 26 | 27 | 28 | [build-system] 29 | requires = ["setuptools"] 30 | build-backend = "setuptools.build_meta" 31 | 32 | [tool.pylint.'MESSAGES CONTROL'] 33 | extension-pkg-whitelist = "pydantic" 34 | 35 | [tool.mypy] 36 | files = [ 37 | "openapi_client", 38 | #"test", # auto-generated tests 39 | "tests", # hand-written tests 40 | ] 41 | # TODO: enable "strict" once all these individual checks are passing 42 | # strict = true 43 | 44 | # List from: https://mypy.readthedocs.io/en/stable/existing_code.html#introduce-stricter-options 45 | warn_unused_configs = true 46 | warn_redundant_casts = true 47 | warn_unused_ignores = true 48 | 49 | ## Getting these passing should be easy 50 | strict_equality = true 51 | strict_concatenate = true 52 | 53 | ## Strongly recommend enabling this one as soon as you can 54 | check_untyped_defs = true 55 | 56 | ## These shouldn't be too much additional work, but may be tricky to 57 | ## get passing if you use a lot of untyped libraries 58 | disallow_subclassing_any = true 59 | disallow_untyped_decorators = true 60 | disallow_any_generics = true 61 | 62 | ### These next few are various gradations of forcing use of type annotations 63 | #disallow_untyped_calls = true 64 | #disallow_incomplete_defs = true 65 | #disallow_untyped_defs = true 66 | # 67 | ### This one isn't too hard to get passing, but return on investment is lower 68 | #no_implicit_reexport = true 69 | # 70 | ### This one can be tricky to get passing if you use a lot of untyped libraries 71 | #warn_return_any = true 72 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/requirements.txt: -------------------------------------------------------------------------------- 1 | python_dateutil >= 2.5.3 2 | setuptools >= 21.0.0 3 | urllib3 >= 1.25.3, < 2.1.0 4 | pydantic >= 2 5 | typing-extensions >= 4.7.1 6 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length=99 3 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/setup.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | from setuptools import setup, find_packages # noqa: H301 16 | 17 | # To install the library, run the following 18 | # 19 | # python setup.py install 20 | # 21 | # prerequisite: setuptools 22 | # http://pypi.python.org/pypi/setuptools 23 | NAME = "openapi-client" 24 | VERSION = "1.0.0" 25 | PYTHON_REQUIRES = ">=3.7" 26 | REQUIRES = [ 27 | "urllib3 >= 1.25.3, < 2.1.0", 28 | "python-dateutil", 29 | "pydantic >= 2", 30 | "typing-extensions >= 4.7.1", 31 | ] 32 | 33 | setup( 34 | name=NAME, 35 | version=VERSION, 36 | description="Open Message Format", 37 | author="OpenAPI Generator community", 38 | author_email="team@openapitools.org", 39 | url="", 40 | keywords=["OpenAPI", "OpenAPI-Generator", "Open Message Format"], 41 | install_requires=REQUIRES, 42 | packages=find_packages(exclude=["test", "tests"]), 43 | include_package_data=True, 44 | long_description_content_type='text/markdown', 45 | long_description="""\ 46 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 47 | """, # noqa: E501 48 | package_data={"openapi_client": ["py.typed"]}, 49 | ) 50 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test-requirements.txt: -------------------------------------------------------------------------------- 1 | pytest~=7.1.3 2 | pytest-cov>=2.8.1 3 | pytest-randomly>=3.12.0 4 | mypy>=1.4.1 5 | types-python-dateutil>=2.8.19 6 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-llm-initiative/open-message-format/c3382c576ede746fc14e1b600a5ed084104bbbae/examples/clients/example_python_client/test/__init__.py -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/test_content_item.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import unittest 16 | 17 | from openapi_client.models.content_item import ContentItem 18 | 19 | class TestContentItem(unittest.TestCase): 20 | """ContentItem unit test stubs""" 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def make_instance(self, include_optional) -> ContentItem: 29 | """Test ContentItem 30 | include_optional is a boolean, when False only required 31 | params are included, when True both required and 32 | optional params are included """ 33 | # uncomment below to create an instance of `ContentItem` 34 | """ 35 | model = ContentItem() 36 | if include_optional: 37 | return ContentItem( 38 | type = '', 39 | text = '', 40 | image_url = None, 41 | source = None 42 | ) 43 | else: 44 | return ContentItem( 45 | type = '', 46 | ) 47 | """ 48 | 49 | def testContentItem(self): 50 | """Test ContentItem""" 51 | # inst_req_only = self.make_instance(include_optional=False) 52 | # inst_req_and_optional = self.make_instance(include_optional=True) 53 | 54 | if __name__ == '__main__': 55 | unittest.main() 56 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/test_default_api.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import unittest 16 | 17 | from openapi_client.api.default_api import DefaultApi 18 | 19 | 20 | class TestDefaultApi(unittest.TestCase): 21 | """DefaultApi unit test stubs""" 22 | 23 | def setUp(self) -> None: 24 | self.api = DefaultApi() 25 | 26 | def tearDown(self) -> None: 27 | pass 28 | 29 | def test_message_post(self) -> None: 30 | """Test case for message_post 31 | 32 | This method allows developers to receive chat messages from an upstream client in a standard format. 33 | """ 34 | pass 35 | 36 | 37 | if __name__ == '__main__': 38 | unittest.main() 39 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/test_message.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import unittest 16 | 17 | from openapi_client.models.message import Message 18 | 19 | class TestMessage(unittest.TestCase): 20 | """Message unit test stubs""" 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def make_instance(self, include_optional) -> Message: 29 | """Test Message 30 | include_optional is a boolean, when False only required 31 | params are included, when True both required and 32 | optional params are included """ 33 | # uncomment below to create an instance of `Message` 34 | """ 35 | model = Message() 36 | if include_optional: 37 | return Message( 38 | role = '', 39 | content = None 40 | ) 41 | else: 42 | return Message( 43 | role = '', 44 | content = None, 45 | ) 46 | """ 47 | 48 | def testMessage(self): 49 | """Test Message""" 50 | # inst_req_only = self.make_instance(include_optional=False) 51 | # inst_req_and_optional = self.make_instance(include_optional=True) 52 | 53 | if __name__ == '__main__': 54 | unittest.main() 55 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/test_message_content.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import unittest 16 | 17 | from openapi_client.models.message_content import MessageContent 18 | 19 | class TestMessageContent(unittest.TestCase): 20 | """MessageContent unit test stubs""" 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def make_instance(self, include_optional) -> MessageContent: 29 | """Test MessageContent 30 | include_optional is a boolean, when False only required 31 | params are included, when True both required and 32 | optional params are included """ 33 | # uncomment below to create an instance of `MessageContent` 34 | """ 35 | model = MessageContent() 36 | if include_optional: 37 | return MessageContent( 38 | ) 39 | else: 40 | return MessageContent( 41 | ) 42 | """ 43 | 44 | def testMessageContent(self): 45 | """Test MessageContent""" 46 | # inst_req_only = self.make_instance(include_optional=False) 47 | # inst_req_and_optional = self.make_instance(include_optional=True) 48 | 49 | if __name__ == '__main__': 50 | unittest.main() 51 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/test_metadata.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import unittest 16 | 17 | from openapi_client.models.metadata import Metadata 18 | 19 | class TestMetadata(unittest.TestCase): 20 | """Metadata unit test stubs""" 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def make_instance(self, include_optional) -> Metadata: 29 | """Test Metadata 30 | include_optional is a boolean, when False only required 31 | params are included, when True both required and 32 | optional params are included """ 33 | # uncomment below to create an instance of `Metadata` 34 | """ 35 | model = Metadata() 36 | if include_optional: 37 | return Metadata( 38 | model = '', 39 | tokens = '' 40 | ) 41 | else: 42 | return Metadata( 43 | model = '', 44 | ) 45 | """ 46 | 47 | def testMetadata(self): 48 | """Test Metadata""" 49 | # inst_req_only = self.make_instance(include_optional=False) 50 | # inst_req_and_optional = self.make_instance(include_optional=True) 51 | 52 | if __name__ == '__main__': 53 | unittest.main() 54 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/test/test_response_message.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | """ 4 | Open Message Format 5 | 6 | This specification defines an API contract between client and server for building conversational agents, and defines a standard schema for the \"messages\" object, which contains user and assistant interactions. The \"messages\" object schema serves a dual purpose, to capture user and assistant conversation from client to your back-end, and from your back-end server to an upstream LLM 7 | 8 | The version of the OpenAPI document: 1.0.0 9 | Generated by OpenAPI Generator (https://openapi-generator.tech) 10 | 11 | Do not edit the class manually. 12 | """ # noqa: E501 13 | 14 | 15 | import unittest 16 | 17 | from openapi_client.models.response_message import ResponseMessage 18 | 19 | class TestResponseMessage(unittest.TestCase): 20 | """ResponseMessage unit test stubs""" 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def tearDown(self): 26 | pass 27 | 28 | def make_instance(self, include_optional) -> ResponseMessage: 29 | """Test ResponseMessage 30 | include_optional is a boolean, when False only required 31 | params are included, when True both required and 32 | optional params are included """ 33 | # uncomment below to create an instance of `ResponseMessage` 34 | """ 35 | model = ResponseMessage() 36 | if include_optional: 37 | return ResponseMessage( 38 | content = '', 39 | refusal = '', 40 | role = '', 41 | metadata = openapi_client.models.metadata.Metadata( 42 | model = '', 43 | tokens = '', ) 44 | ) 45 | else: 46 | return ResponseMessage( 47 | content = '', 48 | role = '', 49 | ) 50 | """ 51 | 52 | def testResponseMessage(self): 53 | """Test ResponseMessage""" 54 | # inst_req_only = self.make_instance(include_optional=False) 55 | # inst_req_and_optional = self.make_instance(include_optional=True) 56 | 57 | if __name__ == '__main__': 58 | unittest.main() 59 | -------------------------------------------------------------------------------- /examples/clients/example_python_client/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py3 3 | 4 | [testenv] 5 | deps=-r{toxinidir}/requirements.txt 6 | -r{toxinidir}/test-requirements.txt 7 | 8 | commands= 9 | pytest --cov=openapi_client 10 | -------------------------------------------------------------------------------- /examples/conversion_tools/README.md: -------------------------------------------------------------------------------- 1 | # OMF Conversion examples 2 | Included in this folder are different examples of how to convert OMF into another format like AWS Bedrock's message format. -------------------------------------------------------------------------------- /examples/conversion_tools/conversion-tools-py/bedrock.py: -------------------------------------------------------------------------------- 1 | """ 2 | This Python script demonstrates the conversion of messages from the Open Message Format (OMF) 3 | to the AWS Bedrock message format. The script includes a conversion function that processes 4 | OMF messages, which may contain text and/or images (in Base64 format), and converts them into 5 | a format compatible with AWS Bedrock. 6 | 7 | The script also includes test cases to validate the conversion function. These tests check 8 | the conversion of: 9 | - Text-only messages 10 | - Image-only messages (PNG) 11 | - Mixed content messages (both text and image) 12 | - Messages with non-PNG images (JPEG, GIF, WebP) 13 | """ 14 | 15 | from typing import List, Dict 16 | import logging 17 | import boto3 18 | 19 | from botocore.exceptions import ClientError 20 | 21 | 22 | logger = logging.getLogger(__name__) 23 | logging.basicConfig(level=logging.INFO) 24 | 25 | 26 | def generate_conversation(bedrock_client, model_id, messages): 27 | """ 28 | Sends messages to a model. 29 | Args: 30 | bedrock_client: The Boto3 Bedrock runtime client. 31 | model_id (str): The model ID to use. 32 | system_prompts (JSON) : The system prompts for the model to use. 33 | messages (JSON) : The messages to send to the model. 34 | 35 | Returns: 36 | response (JSON): The conversation that the model generated. 37 | 38 | """ 39 | 40 | logger.info("Generating message with model %s", model_id) 41 | 42 | # Send the message. 43 | response = bedrock_client.converse( 44 | modelId=model_id, 45 | messages=messages, 46 | ) 47 | 48 | # Log token usage. 49 | token_usage = response["usage"] 50 | logger.info("Input tokens: %s", token_usage["inputTokens"]) 51 | logger.info("Output tokens: %s", token_usage["outputTokens"]) 52 | logger.info("Total tokens: %s", token_usage["totalTokens"]) 53 | logger.info("Stop reason: %s", response["stopReason"]) 54 | 55 | return response 56 | 57 | 58 | # Function to convert a list of OMF messages to Bedrock messages 59 | def convert_omf_to_bedrock(messages: List[Dict]) -> List[Dict]: 60 | """ 61 | Converts a list of OMF messages to Bedrock messages. 62 | 63 | Args: 64 | messages (List[Dict]): A list of OMF message dictionaries. 65 | 66 | Returns: 67 | List[Dict]: A list of Bedrock message dictionaries. 68 | """ 69 | bedrock_messages = [] 70 | 71 | for message in messages: 72 | role = message["role"] 73 | bedrock_content = [] 74 | 75 | # Check if the content is a list (complex content) 76 | if isinstance(message["content"], list): 77 | for item in message["content"]: 78 | # Handle text content 79 | if item["type"] == "text": 80 | bedrock_content.append({"text": item["text"]}) 81 | # Handle image content 82 | elif item["type"] == "image": 83 | # Map the media type to a simpler format string 84 | format_map = { 85 | "image/png": "png", 86 | "image/jpeg": "jpeg", 87 | "image/gif": "gif", 88 | "image/webp": "webp", 89 | } 90 | format = format_map.get(item["source"]["media_type"], "unknown") 91 | bedrock_content.append( 92 | { 93 | "image": { 94 | "format": format, 95 | "source": {"bytes": item["source"]["data"]}, 96 | } 97 | } 98 | ) 99 | 100 | # Add the converted message to the list 101 | bedrock_messages.append({"role": role, "content": bedrock_content}) 102 | 103 | return bedrock_messages 104 | 105 | 106 | # Test case for converting a text-only message 107 | def test_convert_text_only(): 108 | """ 109 | Tests the conversion of a text-only OMF message to Bedrock format. 110 | """ 111 | omf_messages = [ 112 | {"role": "user", "content": [{"type": "text", "text": "Hello, World!"}]} 113 | ] 114 | 115 | expected_bedrock_messages = [ 116 | {"role": "user", "content": [{"text": "Hello, World!"}]} 117 | ] 118 | 119 | assert convert_omf_to_bedrock(omf_messages) == expected_bedrock_messages 120 | 121 | 122 | # Test case for converting an image-only (PNG) message 123 | def test_convert_image_only(): 124 | """ 125 | Tests the conversion of an image-only OMF message (PNG) to Bedrock format. 126 | """ 127 | omf_messages = [ 128 | { 129 | "role": "user", 130 | "content": [ 131 | { 132 | "type": "image", 133 | "source": { 134 | "type": "base64", 135 | "media_type": "image/png", 136 | "data": "base64_image_data", 137 | }, 138 | } 139 | ], 140 | } 141 | ] 142 | 143 | expected_bedrock_messages = [ 144 | { 145 | "role": "user", 146 | "content": [ 147 | {"image": {"format": "png", "source": {"bytes": "base64_image_data"}}} 148 | ], 149 | } 150 | ] 151 | 152 | assert convert_omf_to_bedrock(omf_messages) == expected_bedrock_messages 153 | 154 | 155 | # Test case for converting a message with both text and image content 156 | def test_convert_text_and_image(): 157 | """ 158 | Tests the conversion of a mixed content OMF message (text and image) to Bedrock format. 159 | """ 160 | omf_messages = [ 161 | { 162 | "role": "user", 163 | "content": [ 164 | { 165 | "type": "image", 166 | "source": { 167 | "type": "base64", 168 | "media_type": "image/png", 169 | "data": "base64_image_data", 170 | }, 171 | }, 172 | {"type": "text", "text": "What is in this image?"}, 173 | ], 174 | } 175 | ] 176 | 177 | expected_bedrock_messages = [ 178 | { 179 | "role": "user", 180 | "content": [ 181 | {"image": {"format": "png", "source": {"bytes": "base64_image_data"}}}, 182 | {"text": "What is in this image?"}, 183 | ], 184 | } 185 | ] 186 | 187 | assert convert_omf_to_bedrock(omf_messages) == expected_bedrock_messages 188 | 189 | 190 | # Test case for converting messages with non-PNG images (JPEG, GIF, WebP) 191 | def test_convert_non_png_images(): 192 | """ 193 | Tests the conversion of OMF messages containing non-PNG images (JPEG, GIF, WebP) to Bedrock format. 194 | """ 195 | omf_messages = [ 196 | { 197 | "role": "user", 198 | "content": [ 199 | { 200 | "type": "image", 201 | "source": { 202 | "type": "base64", 203 | "media_type": "image/jpeg", 204 | "data": "base64_jpeg_data", 205 | }, 206 | }, 207 | { 208 | "type": "image", 209 | "source": { 210 | "type": "base64", 211 | "media_type": "image/gif", 212 | "data": "base64_gif_data", 213 | }, 214 | }, 215 | { 216 | "type": "image", 217 | "source": { 218 | "type": "base64", 219 | "media_type": "image/webp", 220 | "data": "base64_webp_data", 221 | }, 222 | }, 223 | ], 224 | } 225 | ] 226 | 227 | expected_bedrock_messages = [ 228 | { 229 | "role": "user", 230 | "content": [ 231 | {"image": {"format": "jpeg", "source": {"bytes": "base64_jpeg_data"}}}, 232 | {"image": {"format": "gif", "source": {"bytes": "base64_gif_data"}}}, 233 | {"image": {"format": "webp", "source": {"bytes": "base64_webp_data"}}}, 234 | ], 235 | } 236 | ] 237 | 238 | assert convert_omf_to_bedrock(omf_messages) == expected_bedrock_messages 239 | 240 | 241 | def test_multiple_messages(): 242 | """ 243 | Tests the conversion of multiple messages to Bedrock format. 244 | """ 245 | omf_messages = [ 246 | {"role": "user", "content": [{"type": "text", "text": "Hello, World!"}]}, 247 | { 248 | "role": "user", 249 | "content": [ 250 | { 251 | "type": "image", 252 | "source": {"media_type": "image/png", "data": "base64_image_data"}, 253 | } 254 | ], 255 | }, 256 | ] 257 | 258 | expected_bedrock_messages = [ 259 | {"role": "user", "content": [{"text": "Hello, World!"}]}, 260 | { 261 | "role": "user", 262 | "content": [ 263 | {"image": {"format": "png", "source": {"bytes": "base64_image_data"}}} 264 | ], 265 | }, 266 | ] 267 | 268 | assert convert_omf_to_bedrock(omf_messages) == expected_bedrock_messages 269 | 270 | 271 | def test_sending_messages_to_aws_bedrock(): 272 | """ 273 | Entrypoint for Anthropic Claude 3 Sonnet example. 274 | """ 275 | 276 | logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") 277 | 278 | model_id = "meta.llama3-8b-instruct-v1:0" 279 | 280 | omf_messages = [ 281 | {"role": "user", "content": [{"type": "text", "text": "Hello, World!"}]} 282 | ] 283 | 284 | messages = convert_omf_to_bedrock(omf_messages) 285 | try: 286 | 287 | bedrock_client = boto3.client(service_name="bedrock-runtime") 288 | response = generate_conversation(bedrock_client, model_id, messages) 289 | 290 | # Add the response message to the conversation. 291 | output_message = response["output"]["message"] 292 | messages.append(output_message) 293 | 294 | # Show the complete conversation. 295 | for message in messages: 296 | print(f"Role: {message['role']}") 297 | for content in message["content"]: 298 | print(f"Text: {content['text']}") 299 | print() 300 | 301 | except ClientError as err: 302 | message = err.response["Error"]["Message"] 303 | logger.error("A client error occurred: %s", message) 304 | raise Exception(f"A client error occured: {message}") 305 | 306 | else: 307 | print(f"Finished generating text with model {model_id}.") 308 | 309 | 310 | # Run all tests 311 | test_convert_text_only() 312 | test_convert_image_only() 313 | test_convert_text_and_image() 314 | test_convert_non_png_images() 315 | test_multiple_messages() 316 | test_sending_messages_to_aws_bedrock() 317 | 318 | print("All tests passed!") 319 | -------------------------------------------------------------------------------- /site_content/after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-llm-initiative/open-message-format/c3382c576ede746fc14e1b600a5ed084104bbbae/site_content/after.png -------------------------------------------------------------------------------- /site_content/before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/open-llm-initiative/open-message-format/c3382c576ede746fc14e1b600a5ed084104bbbae/site_content/before.png --------------------------------------------------------------------------------