# Stubidity
[![Build Status](https://github.com/fmops/stubidity/workflows/Elixir%20CI/badge.svg)](https://github.com/fmops/stubidity/actions/workflows/elixir.yml) [![Hex.pm](https://img.shields.io/hexpm/v/stubidity.svg)](https://hex.pm/packages/stubidity) [![Documentation](https://img.shields.io/badge/documentation-gray)](https://hexdocs.pm/stubidity)
## Getting started
The package can be installed by adding `stubidity` to your list of dependencies
in `mix.exs`:
```elixir
def deps do
[
{:stubidity, "~> 0.1.0"}
]
end
```
It can then be mounted in another `Plug.Router`, for example:
```elixir
defmodule ExampleWeb.Router do
use Plug.Router
plug :match
plug :dispatch
forward("/stub", Stubidity)
end
```
## Client usage
To use in your project, set your OpenAI base url to the path where Stubidity is running.
You are welcome to use a public instance of Stubidity at [`https://fmops.ai/api/v1/stub/openai/v1`](https://fmops.ai/api/v1/stub/openai/v1). For example:
```python
import openai
openai.api_base = "https://fmops.ai/api/v1/stub/openai/v1"
```
Stubidity aims to provide a faithful API stub, so request headers and bodies must conform to the expected schemas. One notable exception is the API key, where any non-empty value is considered valid.
To create a ChatGPT completion with `curl`:
```sh
curl \
-H "content-type: application/json" \
-H "authorization: bearer foo" \
-X POST \
https://fmops.ai/api/v1/stub/openai/v1/chat/completions \
-d '{"model": "gpt-3.5-turbo","messages": [{"role": "system", "message": "tell me a joke"}]}'
```
```
{"choices":[{"finish_reason":null,"index":0,"message":{"content":"Why don't scientists trust atoms? Because they make up everything.","role":"assistant"}}],"created":1682447196,"id":"chatcmpl-abc123","model":"gpt-3.5-turbo-0301","object":"chat.completion","usage":{"completion_tokens":13,"prompt_tokens":3,"total_tokens":16}}
```
Streaming is also supported:
```sh
curl \
-H "content-type: application/json" \
-H "authorization: bearer foo" \
-X POST \
https://fmops.ai/api/v1/stub/openai/v1/chat/completions \
-d '{"model": "gpt-3.5-turbo","messages": [{"role": "system", "message": "tell me a joke"}]}'
```
```
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"role":"assistant"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"Why"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" was"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" the"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" math"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" book"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" sad"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"?\n\n"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"Because"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" it"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" had"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" too"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" many"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":" problems"},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{"content":"."},"index":0,"finish_reason":null}]}
data: {"id":"chatcmpl-6zwEy96ojYcDRcdNX8f4L9fu95CaJ","object":"chat.completion.chunk","created":1680219904,"model":"gpt-3.5-turbo-0301","choices":[{"delta":{},"index":0,"finish_reason":"stop"}]}
data: [DONE]
```
## Features and roadmap
- OpenAI
- `/chat/completions` buffered and streaming
### Roadmap
- OpenAI
- `/completions` buffered and streaming
- HuggingFace
- gpt2 buffered
- bloomz buffered and streaming
## Copyright and License
Copyright (c) 2023, Feynman Liang.
Stubidity source code is licensed under the [Apache 2.0 License](LICENSE).