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.
Files changed (193) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/Makefile +134 -0
  4. data/README.md +247 -0
  5. data/SINATRA_COMPAT.md +376 -0
  6. data/bin/tep +2156 -0
  7. data/examples/agentic_chat/README.md +103 -0
  8. data/examples/agentic_chat/app.rb +310 -0
  9. data/examples/api_gateway/README.md +49 -0
  10. data/examples/api_gateway/app.rb +66 -0
  11. data/examples/blog/app.rb +367 -0
  12. data/examples/blog/views/index.erb +36 -0
  13. data/examples/blog/views/login.erb +28 -0
  14. data/examples/blog/views/new_post.erb +25 -0
  15. data/examples/blog/views/show.erb +16 -0
  16. data/examples/chat/app.rb +278 -0
  17. data/examples/chat/assets/logo.svg +13 -0
  18. data/examples/chat/assets/style.css +209 -0
  19. data/examples/chat/views/index.erb +142 -0
  20. data/examples/chatbot/README.md +111 -0
  21. data/examples/chatbot/app.rb +1024 -0
  22. data/examples/chatbot/assets/chat.js +249 -0
  23. data/examples/chatbot/assets/compare.js +93 -0
  24. data/examples/chatbot/assets/markdown.js +84 -0
  25. data/examples/chatbot/assets/style.css +215 -0
  26. data/examples/chatbot/schema.sql +25 -0
  27. data/examples/chatbot/views/compare.erb +43 -0
  28. data/examples/chatbot/views/index.erb +42 -0
  29. data/examples/chatbot/views/login.erb +22 -0
  30. data/examples/chatbot/views/setup.erb +23 -0
  31. data/examples/counter/README.md +68 -0
  32. data/examples/counter/app.rb +85 -0
  33. data/examples/experiments/AGENTS.md +91 -0
  34. data/examples/experiments/README.md +99 -0
  35. data/examples/experiments/app.rb +225 -0
  36. data/examples/geohash/Gemfile +11 -0
  37. data/examples/geohash/Gemfile.lock +17 -0
  38. data/examples/geohash/README.md +58 -0
  39. data/examples/geohash/app.rb +33 -0
  40. data/examples/hello.rb +120 -0
  41. data/examples/llm_gateway/README.md +73 -0
  42. data/examples/llm_gateway/app.rb +91 -0
  43. data/examples/maidenhead/Gemfile +7 -0
  44. data/examples/maidenhead/Gemfile.lock +17 -0
  45. data/examples/maidenhead/README.md +47 -0
  46. data/examples/maidenhead/app.rb +46 -0
  47. data/examples/pg_hello.rb +76 -0
  48. data/examples/qdrant/Gemfile +11 -0
  49. data/examples/qdrant/Gemfile.lock +29 -0
  50. data/examples/qdrant/README.md +54 -0
  51. data/examples/sinatra_style.rb +32 -0
  52. data/examples/websocket_echo.rb +37 -0
  53. data/lib/tep/agent_delegation.rb +35 -0
  54. data/lib/tep/app.rb +291 -0
  55. data/lib/tep/assets.rb +52 -0
  56. data/lib/tep/auth.rb +78 -0
  57. data/lib/tep/auth_bearer_token.rb +126 -0
  58. data/lib/tep/auth_oauth2.rb +189 -0
  59. data/lib/tep/auth_oauth2_client.rb +29 -0
  60. data/lib/tep/auth_oauth2_code.rb +40 -0
  61. data/lib/tep/auth_session_cookie.rb +132 -0
  62. data/lib/tep/broadcast.rb +265 -0
  63. data/lib/tep/broadcast_subscription.rb +42 -0
  64. data/lib/tep/cache.rb +49 -0
  65. data/lib/tep/events.rb +257 -0
  66. data/lib/tep/filter.rb +21 -0
  67. data/lib/tep/handler.rb +35 -0
  68. data/lib/tep/http.rb +599 -0
  69. data/lib/tep/identity.rb +67 -0
  70. data/lib/tep/job.rb +186 -0
  71. data/lib/tep/json.rb +572 -0
  72. data/lib/tep/jwt.rb +126 -0
  73. data/lib/tep/live_view.rb +219 -0
  74. data/lib/tep/llm.rb +505 -0
  75. data/lib/tep/logger.rb +85 -0
  76. data/lib/tep/mcp.rb +203 -0
  77. data/lib/tep/multipart.rb +98 -0
  78. data/lib/tep/net.rb +155 -0
  79. data/lib/tep/openai_server.rb +725 -0
  80. data/lib/tep/parallel.rb +168 -0
  81. data/lib/tep/parser.rb +81 -0
  82. data/lib/tep/password.rb +102 -0
  83. data/lib/tep/pg.rb +1128 -0
  84. data/lib/tep/presence.rb +589 -0
  85. data/lib/tep/presence_entry.rb +52 -0
  86. data/lib/tep/proxy.rb +801 -0
  87. data/lib/tep/request.rb +194 -0
  88. data/lib/tep/response.rb +134 -0
  89. data/lib/tep/router.rb +137 -0
  90. data/lib/tep/scheduler.rb +342 -0
  91. data/lib/tep/security.rb +140 -0
  92. data/lib/tep/server.rb +276 -0
  93. data/lib/tep/server_scheduled.rb +375 -0
  94. data/lib/tep/session.rb +98 -0
  95. data/lib/tep/shell.rb +62 -0
  96. data/lib/tep/sphttp.c +858 -0
  97. data/lib/tep/sqlite.rb +215 -0
  98. data/lib/tep/streamer.rb +31 -0
  99. data/lib/tep/tep_pg.c +769 -0
  100. data/lib/tep/tep_sqlite.c +320 -0
  101. data/lib/tep/url.rb +161 -0
  102. data/lib/tep/version.rb +3 -0
  103. data/lib/tep/websocket/connection.rb +171 -0
  104. data/lib/tep/websocket/driver.rb +169 -0
  105. data/lib/tep/websocket/frame.rb +238 -0
  106. data/lib/tep/websocket/handshake.rb +159 -0
  107. data/lib/tep/websocket.rb +68 -0
  108. data/lib/tep.rb +981 -0
  109. data/public/hello.txt +1 -0
  110. data/public/style.css +4 -0
  111. data/spinel-ext.json +33 -0
  112. data/test/helper.rb +248 -0
  113. data/test/real_world/01_simple.rb +5 -0
  114. data/test/real_world/02_lifecycle.rb +20 -0
  115. data/test/real_world/03_chat.rb +75 -0
  116. data/test/real_world/04_health_api.rb +25 -0
  117. data/test/real_world/05_todo_api.rb +57 -0
  118. data/test/real_world/06_basic_auth.rb +25 -0
  119. data/test/real_world/07_bbc_rest_api.rb +228 -0
  120. data/test/real_world/07_sklise_things.rb +109 -0
  121. data/test/real_world/08_jwd83_helloworld.rb +56 -0
  122. data/test/run_all.rb +7 -0
  123. data/test/run_parallel.rb +89 -0
  124. data/test/spinel_scheduled_burst_segv_repro.rb +33 -0
  125. data/test/test_api_gateway.rb +76 -0
  126. data/test/test_auth.rb +223 -0
  127. data/test/test_auth_oauth2.rb +208 -0
  128. data/test/test_auth_session_cookie.rb +198 -0
  129. data/test/test_broadcast.rb +197 -0
  130. data/test/test_broadcast_pg.rb +135 -0
  131. data/test/test_cache.rb +98 -0
  132. data/test/test_cache_static.rb +48 -0
  133. data/test/test_cookies.rb +52 -0
  134. data/test/test_erb.rb +53 -0
  135. data/test/test_erb_ivars.rb +58 -0
  136. data/test/test_events.rb +114 -0
  137. data/test/test_filters.rb +41 -0
  138. data/test/test_geohash_example.rb +89 -0
  139. data/test/test_http.rb +137 -0
  140. data/test/test_http_pool.rb +122 -0
  141. data/test/test_http_pool_send.rb +57 -0
  142. data/test/test_identity.rb +165 -0
  143. data/test/test_inbound_tls.rb +101 -0
  144. data/test/test_inbound_tls_scheduled.rb +101 -0
  145. data/test/test_job.rb +108 -0
  146. data/test/test_json.rb +168 -0
  147. data/test/test_jwt.rb +143 -0
  148. data/test/test_live_view.rb +324 -0
  149. data/test/test_llm.rb +250 -0
  150. data/test/test_llm_gateway.rb +95 -0
  151. data/test/test_logger.rb +101 -0
  152. data/test/test_maidenhead_example.rb +86 -0
  153. data/test/test_mcp.rb +264 -0
  154. data/test/test_misc_v02.rb +54 -0
  155. data/test/test_modular.rb +43 -0
  156. data/test/test_multi_filters.rb +40 -0
  157. data/test/test_mustache.rb +57 -0
  158. data/test/test_openai_server.rb +598 -0
  159. data/test/test_optional_segments.rb +45 -0
  160. data/test/test_parallel.rb +102 -0
  161. data/test/test_params.rb +99 -0
  162. data/test/test_pass.rb +42 -0
  163. data/test/test_password.rb +101 -0
  164. data/test/test_pg.rb +673 -0
  165. data/test/test_presence.rb +374 -0
  166. data/test/test_presence_pg.rb +309 -0
  167. data/test/test_proxy.rb +556 -0
  168. data/test/test_proxy_dsl.rb +119 -0
  169. data/test/test_proxy_streaming.rb +146 -0
  170. data/test/test_real_world.rb +397 -0
  171. data/test/test_regex_routes.rb +52 -0
  172. data/test/test_request_methods.rb +102 -0
  173. data/test/test_response.rb +123 -0
  174. data/test/test_routing.rb +109 -0
  175. data/test/test_scheduler.rb +153 -0
  176. data/test/test_security.rb +72 -0
  177. data/test/test_server_scheduled.rb +56 -0
  178. data/test/test_sessions.rb +59 -0
  179. data/test/test_shell.rb +54 -0
  180. data/test/test_sqlite.rb +148 -0
  181. data/test/test_sqlite_cached.rb +171 -0
  182. data/test/test_static.rb +57 -0
  183. data/test/test_streaming.rb +96 -0
  184. data/test/test_unsupported.rb +32 -0
  185. data/test/test_websocket.rb +152 -0
  186. data/test/test_websocket_echo.rb +138 -0
  187. data/test/views/greet.erb +5 -0
  188. data/test/views/hello.erb +5 -0
  189. data/test/views/list.erb +5 -0
  190. data/test/views/m_ivars.mustache +3 -0
  191. data/test/views/m_simple.mustache +4 -0
  192. data/test/views/mixed.erb +3 -0
  193. 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).