From dd9c81eee32b6aefd670252ad0fa08170a3cd3e8 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Wed, 19 Mar 2025 19:45:20 -0400 Subject: [PATCH] ship it. new file: .gitignore new file: README.md new file: requirements.txt new file: static/openapi.yaml --- .gitignore | 136 +++++++++++++++ README.md | 134 +++++++++++++++ requirements.txt | 5 + static/openapi.yaml | 398 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 673 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 requirements.txt create mode 100644 static/openapi.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1af19e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,136 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local +vars.sh + +# ignore python virtualenvs +env/ +venv/ + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/README.md b/README.md new file mode 100644 index 0000000..ccd3ea3 --- /dev/null +++ b/README.md @@ -0,0 +1,134 @@ +# SLOP Streamlit Example + +Streamlit-based SLOP example with dynamic model endpoints. It explains the purpose, setup, usage, and structure in a clear and concise way. + +This is a Python implementation of the [SLOP pattern](https://github.com/agnt-gg/slop) using Flask as a backend server and Streamlit as a frontend interface. It dynamically discovers and utilizes language models from OpenAI-compatible endpoints (e.g., vLLM, Ollama, etc.) specified via environment variables. + +## Features + +- **Chat**: Send messages to dynamically discovered AI models. +- **Tools**: Use simple tools like a calculator and greeter. (broken, please help fix this) +- **Memory**: Store and retrieve key-value pairs. +- **Resources**: Access predefined static content. +- **Pay**: Simulate a payment transaction. + +## Prerequisites + +- Python 3.8+ +- A terminal to run commands +- Optional: Access to OpenAI-compatible model endpoints (e.g., `https://hermes.ai.unturf.com/v1`) + +## Setup + +1. **Clone the Repository**: + ```bash + git clone https://github.com/agnt-gg/slop + cd slop/examples/streamlit + ``` + +2. **Set Up Virtual Environment**: + + ```bash + make setup + ``` + This creates a virtual environment (`venv`) and installs dependencies from `requirements.txt`. + +3. **Configure Model Endpoints**: + Edit `vars.sh` to specify your model endpoints: + + ```bash + # vars.sh + export MODEL_ENDPOINT_0=https://hermes.ai.unturf.com/v1 + export MODEL_ENDPOINT_1=https://node2.naptha.ai/inference + export MODEL_ENDPOINT_2=https://node3.naptha.ai/inference + ``` + - Gaps in numbering (e.g., skipping `MODEL_ENDPOINT_1`) are supported. + - API keys are optional; defaults to `"not-needed"` if unset (e.g., `export MODEL_API_KEY_0=your-key`). + +## Usage + +1. **Run the Flask Server**: + Open a terminal and start the backend: + + ```bash + make slop-flask + ``` + - This sources `vars.sh` and runs `slop_with_models.py` on `http://localhost:31337`. + - Logs will show model discovery (e.g., `Loaded models: [model1, endpoint_7:default]`). + +2. **Run the Streamlit App**: + Open a second terminal and start the frontend: + + ```bash + make slop-streamlit + ``` + - Opens in your browser at `http://localhost:8501`. + - Displays a UI with Chat, Tools, Memory, Resources, and Pay sections. + +3. **Interact**: + - **Chat**: Select a model from the dropdown and send a message. + - **Tools**: Use the calculator or greeter. + - **Memory**: Store/retrieve values. + - **Resources**: View static content. + - **Pay**: Simulate a transaction. + +4. **Clean Up** (optional): + + ```bash + make clean + ``` + Removes the virtual environment. + +## Files + +- **`slop_with_models.py`**: Flask server implementing the SLOP pattern with dynamic model discovery. +- **`streamlit_slop_with_models.py`**: Streamlit frontend for user interaction. +- **`Makefile`**: Simplifies ``make setup`` and running with ``make slop-flask`` and ``make slop-streamlit``. +- **`vars.sh`**: Environment variables for model endpoints. Feel free to start with ``vars.sh.sample``! +- **`requirements.txt`**: Dependencies + +## How It Works + +1. **Model Discovery**: + - The Flask server scans `MODEL_ENDPOINT_0` to `MODEL_ENDPOINT_999` from `vars.sh`. + - Queries each endpoint’s `/v1/models` using the OpenAI client. + - Maps model IDs to their respective clients + +2. **API Endpoints**: + - `/models`: Returns the list of discovered models. + - `/chat`: Handles chat completions with the selected model. + - `/tools`, `/memory`, `/resources`, `/pay`: Implement SLOP pattern features. + +3. **Frontend**: + - Streamlit fetches the model list from `/models` and provides a dropdown. + - Sends requests to Flask for chat and other functionalities. + +## Troubleshooting + +- **No Models in Dropdown**: + - Check Flask logs (`make slop-flask`) for errors (e.g., `Failed to list models for endpoint_X`). + - Test endpoints with `curl /v1/models` to ensure they’re OpenAI-compatible. +- **Server Not Responding**: + - Ensure Flask is running (`make slop-flask`) before starting Streamlit. +- **Environment Variables**: + - Verify `vars.sh` is correct and sourced (`source vars.sh; echo $MODEL_ENDPOINT_0`). + +## Dependencies + +Listed in `requirements.txt`: +- `flask`: Backend server +- `streamlit`: Frontend UI +- `openai`: Client for model endpoints +- `requests`: HTTP requests in Streamlit + +## Learn More + +- [SLOP Specification](https://github.com/agnt-gg/slop) + +This example demonstrates a flexible, extensible SLOP implementation with a modern UI. Contributions and feedback are welcome! + +--- + +This is research into the genesis of of the future https://slop.unturf.com/ + +The code in this example is Public Domain. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..766b000 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +flask +streamlit +openai +requests +flask_swagger_ui diff --git a/static/openapi.yaml b/static/openapi.yaml new file mode 100644 index 0000000..dc9fafd --- /dev/null +++ b/static/openapi.yaml @@ -0,0 +1,398 @@ +openapi: 3.0.0 +info: + title: SLOP API + description: A SLOP pattern implementation with dynamic model endpoints + version: 1.0.0 +servers: + - url: http://localhost:31337 + description: Local development server +paths: + /chat: + post: + summary: Send a message to an AI model + tags: + - Chat + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ChatRequest' + responses: + '200': + description: Successful response with AI message + content: + application/json: + schema: + $ref: '#/components/schemas/ChatResponse' + '404': + description: Model not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /models: + get: + summary: List available models + tags: + - Models + responses: + '200': + description: List of model IDs + content: + application/json: + schema: + $ref: '#/components/schemas/ModelsResponse' + /tools: + get: + summary: List available tools + tags: + - Tools + responses: + '200': + description: List of tools + content: + application/json: + schema: + $ref: '#/components/schemas/ToolsResponse' + /tools/{tool_id}: + post: + summary: Use a specific tool + tags: + - Tools + parameters: + - name: tool_id + in: path + required: true + schema: + type: string + enum: [calculator, greet] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ToolRequest' + responses: + '200': + description: Tool execution result + content: + application/json: + schema: + $ref: '#/components/schemas/ToolResponse' + '400': + description: Missing required parameter + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Tool not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /memory: + get: + summary: List all memory keys + tags: + - Memory + responses: + '200': + description: List of memory keys + content: + application/json: + schema: + $ref: '#/components/schemas/MemoryListResponse' + post: + summary: Store a key-value pair + tags: + - Memory + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MemoryStoreRequest' + responses: + '200': + description: Successfully stored + content: + application/json: + schema: + $ref: '#/components/schemas/MemoryStoreResponse' + /memory/{key}: + get: + summary: Retrieve a value by key + tags: + - Memory + parameters: + - name: key + in: path + required: true + schema: + type: string + responses: + '200': + description: Retrieved value + content: + application/json: + schema: + $ref: '#/components/schemas/MemoryGetResponse' + delete: + summary: Delete a memory key + tags: + - Memory + parameters: + - name: key + in: path + required: true + schema: + type: string + responses: + '200': + description: Successfully deleted + content: + application/json: + schema: + $ref: '#/components/schemas/MemoryStoreResponse' + '404': + description: Key not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /resources: + get: + summary: List available resources + tags: + - Resources + responses: + '200': + description: List of resources + content: + application/json: + schema: + $ref: '#/components/schemas/ResourcesResponse' + /resources/{resource_id}: + get: + summary: Get a specific resource + tags: + - Resources + parameters: + - name: resource_id + in: path + required: true + schema: + type: string + responses: + '200': + description: Resource content + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceResponse' + '404': + description: Resource not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /pay: + post: + summary: Simulate a payment + tags: + - Pay + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PayRequest' + responses: + '200': + description: Payment simulation result + content: + application/json: + schema: + $ref: '#/components/schemas/PayResponse' +components: + schemas: + ChatRequest: + type: object + properties: + messages: + type: array + items: + $ref: '#/components/schemas/Message' + model: + type: string + nullable: true + required: + - messages + Message: + type: object + properties: + role: + type: string + enum: [user, assistant, system] + content: + type: string + required: + - role + - content + ChatResponse: + type: object + properties: + choices: + type: array + items: + type: object + properties: + message: + type: object + properties: + content: + type: string + required: + - message + required: + - choices + ModelsResponse: + type: object + properties: + models: + type: array + items: + type: string + required: + - models + ToolsResponse: + type: object + properties: + tools: + type: array + items: + type: object + properties: + id: + type: string + description: + type: string + required: + - id + - description + required: + - tools + ToolRequest: + type: object + properties: + expression: + type: string + nullable: true + name: + type: string + nullable: true + ToolResponse: + type: object + properties: + result: + oneOf: + - type: string + - type: integer + required: + - result + MemoryStoreRequest: + type: object + properties: + key: + type: string + value: + type: string + required: + - key + - value + MemoryStoreResponse: + type: object + properties: + status: + type: string + enum: [stored, deleted] + required: + - status + MemoryGetResponse: + type: object + properties: + value: + type: string + nullable: true + required: + - value + MemoryListResponse: + type: object + properties: + keys: + type: array + items: + type: string + required: + - keys + ResourcesResponse: + type: object + properties: + resources: + type: array + items: + type: object + properties: + id: + type: string + content: + type: string + required: + - id + - content + required: + - resources + ResourceResponse: + type: object + properties: + id: + type: string + content: + type: string + required: + - id + - content + PayRequest: + type: object + properties: + amount: + type: number + format: float + required: + - amount + PayResponse: + type: object + properties: + transaction_id: + type: string + status: + type: string + enum: [success] + required: + - transaction_id + - status + ErrorResponse: + type: object + properties: + error: + type: string + required: + - error