tep 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/Makefile +134 -0
- data/README.md +247 -0
- data/SINATRA_COMPAT.md +376 -0
- data/bin/tep +2156 -0
- data/examples/agentic_chat/README.md +103 -0
- data/examples/agentic_chat/app.rb +310 -0
- data/examples/api_gateway/README.md +49 -0
- data/examples/api_gateway/app.rb +66 -0
- data/examples/blog/app.rb +367 -0
- data/examples/blog/views/index.erb +36 -0
- data/examples/blog/views/login.erb +28 -0
- data/examples/blog/views/new_post.erb +25 -0
- data/examples/blog/views/show.erb +16 -0
- data/examples/chat/app.rb +278 -0
- data/examples/chat/assets/logo.svg +13 -0
- data/examples/chat/assets/style.css +209 -0
- data/examples/chat/views/index.erb +142 -0
- data/examples/chatbot/README.md +111 -0
- data/examples/chatbot/app.rb +1024 -0
- data/examples/chatbot/assets/chat.js +249 -0
- data/examples/chatbot/assets/compare.js +93 -0
- data/examples/chatbot/assets/markdown.js +84 -0
- data/examples/chatbot/assets/style.css +215 -0
- data/examples/chatbot/schema.sql +25 -0
- data/examples/chatbot/views/compare.erb +43 -0
- data/examples/chatbot/views/index.erb +42 -0
- data/examples/chatbot/views/login.erb +22 -0
- data/examples/chatbot/views/setup.erb +23 -0
- data/examples/counter/README.md +68 -0
- data/examples/counter/app.rb +85 -0
- data/examples/experiments/AGENTS.md +91 -0
- data/examples/experiments/README.md +99 -0
- data/examples/experiments/app.rb +225 -0
- data/examples/geohash/Gemfile +11 -0
- data/examples/geohash/Gemfile.lock +17 -0
- data/examples/geohash/README.md +58 -0
- data/examples/geohash/app.rb +33 -0
- data/examples/hello.rb +120 -0
- data/examples/llm_gateway/README.md +73 -0
- data/examples/llm_gateway/app.rb +91 -0
- data/examples/maidenhead/Gemfile +7 -0
- data/examples/maidenhead/Gemfile.lock +17 -0
- data/examples/maidenhead/README.md +47 -0
- data/examples/maidenhead/app.rb +46 -0
- data/examples/pg_hello.rb +76 -0
- data/examples/qdrant/Gemfile +11 -0
- data/examples/qdrant/Gemfile.lock +29 -0
- data/examples/qdrant/README.md +54 -0
- data/examples/sinatra_style.rb +32 -0
- data/examples/websocket_echo.rb +37 -0
- data/lib/tep/agent_delegation.rb +35 -0
- data/lib/tep/app.rb +291 -0
- data/lib/tep/assets.rb +52 -0
- data/lib/tep/auth.rb +78 -0
- data/lib/tep/auth_bearer_token.rb +126 -0
- data/lib/tep/auth_oauth2.rb +189 -0
- data/lib/tep/auth_oauth2_client.rb +29 -0
- data/lib/tep/auth_oauth2_code.rb +40 -0
- data/lib/tep/auth_session_cookie.rb +132 -0
- data/lib/tep/broadcast.rb +265 -0
- data/lib/tep/broadcast_subscription.rb +42 -0
- data/lib/tep/cache.rb +49 -0
- data/lib/tep/events.rb +257 -0
- data/lib/tep/filter.rb +21 -0
- data/lib/tep/handler.rb +35 -0
- data/lib/tep/http.rb +599 -0
- data/lib/tep/identity.rb +67 -0
- data/lib/tep/job.rb +186 -0
- data/lib/tep/json.rb +572 -0
- data/lib/tep/jwt.rb +126 -0
- data/lib/tep/live_view.rb +219 -0
- data/lib/tep/llm.rb +505 -0
- data/lib/tep/logger.rb +85 -0
- data/lib/tep/mcp.rb +203 -0
- data/lib/tep/multipart.rb +98 -0
- data/lib/tep/net.rb +155 -0
- data/lib/tep/openai_server.rb +725 -0
- data/lib/tep/parallel.rb +168 -0
- data/lib/tep/parser.rb +81 -0
- data/lib/tep/password.rb +102 -0
- data/lib/tep/pg.rb +1128 -0
- data/lib/tep/presence.rb +589 -0
- data/lib/tep/presence_entry.rb +52 -0
- data/lib/tep/proxy.rb +801 -0
- data/lib/tep/request.rb +194 -0
- data/lib/tep/response.rb +134 -0
- data/lib/tep/router.rb +137 -0
- data/lib/tep/scheduler.rb +342 -0
- data/lib/tep/security.rb +140 -0
- data/lib/tep/server.rb +276 -0
- data/lib/tep/server_scheduled.rb +375 -0
- data/lib/tep/session.rb +98 -0
- data/lib/tep/shell.rb +62 -0
- data/lib/tep/sphttp.c +858 -0
- data/lib/tep/sqlite.rb +215 -0
- data/lib/tep/streamer.rb +31 -0
- data/lib/tep/tep_pg.c +769 -0
- data/lib/tep/tep_sqlite.c +320 -0
- data/lib/tep/url.rb +161 -0
- data/lib/tep/version.rb +3 -0
- data/lib/tep/websocket/connection.rb +171 -0
- data/lib/tep/websocket/driver.rb +169 -0
- data/lib/tep/websocket/frame.rb +238 -0
- data/lib/tep/websocket/handshake.rb +159 -0
- data/lib/tep/websocket.rb +68 -0
- data/lib/tep.rb +981 -0
- data/public/hello.txt +1 -0
- data/public/style.css +4 -0
- data/spinel-ext.json +33 -0
- data/test/helper.rb +248 -0
- data/test/real_world/01_simple.rb +5 -0
- data/test/real_world/02_lifecycle.rb +20 -0
- data/test/real_world/03_chat.rb +75 -0
- data/test/real_world/04_health_api.rb +25 -0
- data/test/real_world/05_todo_api.rb +57 -0
- data/test/real_world/06_basic_auth.rb +25 -0
- data/test/real_world/07_bbc_rest_api.rb +228 -0
- data/test/real_world/07_sklise_things.rb +109 -0
- data/test/real_world/08_jwd83_helloworld.rb +56 -0
- data/test/run_all.rb +7 -0
- data/test/run_parallel.rb +89 -0
- data/test/spinel_scheduled_burst_segv_repro.rb +33 -0
- data/test/test_api_gateway.rb +76 -0
- data/test/test_auth.rb +223 -0
- data/test/test_auth_oauth2.rb +208 -0
- data/test/test_auth_session_cookie.rb +198 -0
- data/test/test_broadcast.rb +197 -0
- data/test/test_broadcast_pg.rb +135 -0
- data/test/test_cache.rb +98 -0
- data/test/test_cache_static.rb +48 -0
- data/test/test_cookies.rb +52 -0
- data/test/test_erb.rb +53 -0
- data/test/test_erb_ivars.rb +58 -0
- data/test/test_events.rb +114 -0
- data/test/test_filters.rb +41 -0
- data/test/test_geohash_example.rb +89 -0
- data/test/test_http.rb +137 -0
- data/test/test_http_pool.rb +122 -0
- data/test/test_http_pool_send.rb +57 -0
- data/test/test_identity.rb +165 -0
- data/test/test_inbound_tls.rb +101 -0
- data/test/test_inbound_tls_scheduled.rb +101 -0
- data/test/test_job.rb +108 -0
- data/test/test_json.rb +168 -0
- data/test/test_jwt.rb +143 -0
- data/test/test_live_view.rb +324 -0
- data/test/test_llm.rb +250 -0
- data/test/test_llm_gateway.rb +95 -0
- data/test/test_logger.rb +101 -0
- data/test/test_maidenhead_example.rb +86 -0
- data/test/test_mcp.rb +264 -0
- data/test/test_misc_v02.rb +54 -0
- data/test/test_modular.rb +43 -0
- data/test/test_multi_filters.rb +40 -0
- data/test/test_mustache.rb +57 -0
- data/test/test_openai_server.rb +598 -0
- data/test/test_optional_segments.rb +45 -0
- data/test/test_parallel.rb +102 -0
- data/test/test_params.rb +99 -0
- data/test/test_pass.rb +42 -0
- data/test/test_password.rb +101 -0
- data/test/test_pg.rb +673 -0
- data/test/test_presence.rb +374 -0
- data/test/test_presence_pg.rb +309 -0
- data/test/test_proxy.rb +556 -0
- data/test/test_proxy_dsl.rb +119 -0
- data/test/test_proxy_streaming.rb +146 -0
- data/test/test_real_world.rb +397 -0
- data/test/test_regex_routes.rb +52 -0
- data/test/test_request_methods.rb +102 -0
- data/test/test_response.rb +123 -0
- data/test/test_routing.rb +109 -0
- data/test/test_scheduler.rb +153 -0
- data/test/test_security.rb +72 -0
- data/test/test_server_scheduled.rb +56 -0
- data/test/test_sessions.rb +59 -0
- data/test/test_shell.rb +54 -0
- data/test/test_sqlite.rb +148 -0
- data/test/test_sqlite_cached.rb +171 -0
- data/test/test_static.rb +57 -0
- data/test/test_streaming.rb +96 -0
- data/test/test_unsupported.rb +32 -0
- data/test/test_websocket.rb +152 -0
- data/test/test_websocket_echo.rb +138 -0
- data/test/views/greet.erb +5 -0
- data/test/views/hello.erb +5 -0
- data/test/views/list.erb +5 -0
- data/test/views/m_ivars.mustache +3 -0
- data/test/views/m_simple.mustache +4 -0
- data/test/views/mixed.erb +3 -0
- metadata +264 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# examples/chatbot — minimalistic OpenWebUI-style client
|
|
2
|
+
|
|
3
|
+
A single-user, single-conversation chat UI that talks to any
|
|
4
|
+
OpenAI-compatible chat completions backend. Demonstrates the full
|
|
5
|
+
[Tep](https://github.com/OriPekelman/tep) battery surface against a
|
|
6
|
+
real workload.
|
|
7
|
+
|
|
8
|
+
Distinct from [examples/chat/](../chat/) — that's a multi-user
|
|
9
|
+
people-talking-to-people chat with SSE; this one is user-to-LLM.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
# Build once
|
|
15
|
+
bin/tep build examples/chatbot/app.rb -o /tmp/chatbot
|
|
16
|
+
|
|
17
|
+
# Run (defaults to Ollama on localhost:11434, model "llama3")
|
|
18
|
+
/tmp/chatbot -p 4567
|
|
19
|
+
|
|
20
|
+
# Browse to http://localhost:4567/
|
|
21
|
+
# First boot: set a password. Subsequent visits: log in.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The first time you hit `/`, you'll be redirected to `/setup` to pick
|
|
25
|
+
a password. After that, login at `/login`.
|
|
26
|
+
|
|
27
|
+
## Backend selection
|
|
28
|
+
|
|
29
|
+
Three backends interchangeable via env var:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
# Ollama (default)
|
|
33
|
+
CHAT_BACKEND=http://localhost:11434 CHAT_MODEL=llama3 /tmp/chatbot
|
|
34
|
+
|
|
35
|
+
# toy serve (sibling project's OpenAI-compatible server)
|
|
36
|
+
CHAT_BACKEND=http://localhost:8080 CHAT_MODEL=gpt-2 /tmp/chatbot
|
|
37
|
+
|
|
38
|
+
# OpenAI proper
|
|
39
|
+
CHAT_BACKEND=https://api.openai.com CHAT_MODEL=gpt-4 \
|
|
40
|
+
CHAT_API_KEY=sk-... /tmp/chatbot
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
All three speak the same `/v1/chat/completions` shape; the chatbot
|
|
44
|
+
talks to all three identically via `Tep::Llm`.
|
|
45
|
+
|
|
46
|
+
## All env vars
|
|
47
|
+
|
|
48
|
+
| Var | Default | What |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `CHAT_BACKEND` | `http://localhost:11434` | Base URL of the LLM backend |
|
|
51
|
+
| `CHAT_MODEL` | `llama3` | Model name passed to the backend |
|
|
52
|
+
| `CHAT_API_KEY` | `""` | Bearer token (only needed for OpenAI / similar) |
|
|
53
|
+
| `CHAT_SYSTEM_PROMPT` | `""` | Prepended to every conversation if set |
|
|
54
|
+
| `CHAT_DB` | `/tmp/tep_chatbot.db` | SQLite path for password + history |
|
|
55
|
+
| `CHAT_SESSION_SECRET` | `dev-secret-change-me` | HMAC key for the session cookie. Change for any non-localhost deployment. |
|
|
56
|
+
| `CHAT_HSTS` | `0` | HSTS max-age in seconds. Set non-zero ONLY when fronted by HTTPS. |
|
|
57
|
+
|
|
58
|
+
## Tep batteries this exercises
|
|
59
|
+
|
|
60
|
+
Phases A–E are shipped (per [tep#10](https://github.com/OriPekelman/tep/issues/10),
|
|
61
|
+
closed):
|
|
62
|
+
|
|
63
|
+
| Battery | Where |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `Tep::Server::Scheduled` | `set :scheduler, :scheduled` -- fiber-per-connection serving |
|
|
66
|
+
| `Tep::Llm` | `Tep::Llm.new(BACKEND_URL).chat(history)` + `.chat_stream(history)` |
|
|
67
|
+
| `Tep::Http` | (under `Tep::Llm`) the actual HTTP transport |
|
|
68
|
+
| `Tep::SQLite` | conversations + messages + app_config tables |
|
|
69
|
+
| `Tep::Json` | response payloads, manual encoding for nested arrays |
|
|
70
|
+
| `Tep::Password` | first-boot setup + login verify |
|
|
71
|
+
| `Tep::Session` | signed cookie + `authed` flag |
|
|
72
|
+
| `Tep::Assets` | bundled CSS / JS / markdown renderer (served at `/style.css`, `/chat.js`, `/markdown.js` — Tep::Assets paths-relative-to-assets/) |
|
|
73
|
+
| `Tep::Security::Headers` | HSTS + X-Content-Type-Options + X-Frame-Options |
|
|
74
|
+
| `Tep::Security::Cors` | API surface CORS preflight (`/api/v1/...`) |
|
|
75
|
+
| `Tep::Jwt` | API bearer-token auth on `/api/v1/chat/completions` |
|
|
76
|
+
| `Tep::Streamer` | SSE streaming of LLM chunks (the `/api/c/:id/stream` fallback route) |
|
|
77
|
+
| `Tep::WebSocket` | live chat over WS (the default `/api/c/ws` route); `Driver#write` is a Streamer-shape alias so `Tep::Llm.chat_stream` drives the socket directly |
|
|
78
|
+
| `Tep::Job` | background conversation-title summarisation |
|
|
79
|
+
| `Tep::Parallel` | multi-backend compare endpoint (sequential dispatch today; the genuine fork fan-out is blocked on [matz/spinel#575](https://github.com/matz/spinel/issues/575)) |
|
|
80
|
+
| `Tep::Logger` | per-request trace to stderr |
|
|
81
|
+
|
|
82
|
+
Phase F is done (closes [tep#11](https://github.com/OriPekelman/tep/issues/11)):
|
|
83
|
+
the JS client opens one WebSocket to `/api/c/ws` and sends
|
|
84
|
+
`{"conv_id":N,"content":"..."}` per turn; the server emits SSE-
|
|
85
|
+
shaped chunks (`data: {...}\n\n`) per LLM delta as TEXT frames.
|
|
86
|
+
The SSE-shaped wire keeps the parsing loop on both sides — the
|
|
87
|
+
JS code that turned an SSE chunk into a markdown update now
|
|
88
|
+
runs against WS-framed input unchanged. The HTTP SSE route
|
|
89
|
+
(`POST /api/c/:id/stream`) stays as a fallback for older
|
|
90
|
+
browsers / curl debugging.
|
|
91
|
+
|
|
92
|
+
## Source layout
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
examples/chatbot/
|
|
96
|
+
├── app.rb ~280 LOC -- routes + DB + auth
|
|
97
|
+
├── views/
|
|
98
|
+
│ ├── index.erb chat UI shell
|
|
99
|
+
│ ├── login.erb login form
|
|
100
|
+
│ └── setup.erb first-boot password form
|
|
101
|
+
├── assets/
|
|
102
|
+
│ ├── style.css ~150 lines, no framework
|
|
103
|
+
│ ├── chat.js ~120 lines, vanilla, sends + renders
|
|
104
|
+
│ └── markdown.js ~60 lines, hand-rolled subset
|
|
105
|
+
├── schema.sql DDL (also inlined in app.rb)
|
|
106
|
+
└── README.md this file
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Total: ~280 LOC Ruby + ~330 lines of HTML/CSS/JS + the schema. Builds
|
|
110
|
+
to a single ~1.5 MiB native binary; the bundled assets + views are
|
|
111
|
+
compiled in (per `Tep::Assets`'s build-time bundling).
|