llms-py 3.0.0__py3-none-any.whl → 3.0.0b2__py3-none-any.whl

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 (195) hide show
  1. llms/__pycache__/main.cpython-314.pyc +0 -0
  2. llms/index.html +37 -26
  3. llms/llms.json +21 -70
  4. llms/main.py +731 -1426
  5. llms/providers.json +1 -1
  6. llms/{extensions/analytics/ui/index.mjs → ui/Analytics.mjs} +238 -154
  7. llms/ui/App.mjs +63 -133
  8. llms/ui/Avatar.mjs +86 -0
  9. llms/ui/Brand.mjs +52 -0
  10. llms/ui/ChatPrompt.mjs +597 -0
  11. llms/ui/Main.mjs +862 -0
  12. llms/ui/OAuthSignIn.mjs +61 -0
  13. llms/ui/ProviderIcon.mjs +36 -0
  14. llms/ui/ProviderStatus.mjs +104 -0
  15. llms/{extensions/app/ui → ui}/Recents.mjs +57 -82
  16. llms/ui/{modules/chat/SettingsDialog.mjs → SettingsDialog.mjs} +9 -9
  17. llms/{extensions/app/ui/index.mjs → ui/Sidebar.mjs} +57 -122
  18. llms/ui/SignIn.mjs +65 -0
  19. llms/ui/Welcome.mjs +8 -0
  20. llms/ui/ai.mjs +13 -117
  21. llms/ui/app.css +49 -1776
  22. llms/ui/index.mjs +171 -87
  23. llms/ui/lib/charts.mjs +13 -9
  24. llms/ui/lib/servicestack-vue.mjs +3 -3
  25. llms/ui/lib/vue.min.mjs +9 -10
  26. llms/ui/lib/vue.mjs +1602 -1763
  27. llms/ui/markdown.mjs +2 -10
  28. llms/ui/model-selector.mjs +686 -0
  29. llms/ui/tailwind.input.css +1 -55
  30. llms/ui/threadStore.mjs +583 -0
  31. llms/ui/utils.mjs +118 -113
  32. llms/ui.json +1069 -0
  33. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/METADATA +1 -1
  34. llms_py-3.0.0b2.dist-info/RECORD +58 -0
  35. llms/extensions/app/README.md +0 -20
  36. llms/extensions/app/__init__.py +0 -530
  37. llms/extensions/app/__pycache__/__init__.cpython-314.pyc +0 -0
  38. llms/extensions/app/__pycache__/db.cpython-314.pyc +0 -0
  39. llms/extensions/app/__pycache__/db_manager.cpython-314.pyc +0 -0
  40. llms/extensions/app/db.py +0 -644
  41. llms/extensions/app/db_manager.py +0 -195
  42. llms/extensions/app/requests.json +0 -9073
  43. llms/extensions/app/threads.json +0 -15290
  44. llms/extensions/app/ui/threadStore.mjs +0 -411
  45. llms/extensions/core_tools/CALCULATOR.md +0 -32
  46. llms/extensions/core_tools/__init__.py +0 -598
  47. llms/extensions/core_tools/__pycache__/__init__.cpython-314.pyc +0 -0
  48. llms/extensions/core_tools/ui/codemirror/addon/edit/closebrackets.js +0 -201
  49. llms/extensions/core_tools/ui/codemirror/addon/edit/closetag.js +0 -185
  50. llms/extensions/core_tools/ui/codemirror/addon/edit/continuelist.js +0 -101
  51. llms/extensions/core_tools/ui/codemirror/addon/edit/matchbrackets.js +0 -160
  52. llms/extensions/core_tools/ui/codemirror/addon/edit/matchtags.js +0 -66
  53. llms/extensions/core_tools/ui/codemirror/addon/edit/trailingspace.js +0 -27
  54. llms/extensions/core_tools/ui/codemirror/addon/selection/active-line.js +0 -72
  55. llms/extensions/core_tools/ui/codemirror/addon/selection/mark-selection.js +0 -119
  56. llms/extensions/core_tools/ui/codemirror/addon/selection/selection-pointer.js +0 -98
  57. llms/extensions/core_tools/ui/codemirror/doc/docs.css +0 -225
  58. llms/extensions/core_tools/ui/codemirror/doc/source_sans.woff +0 -0
  59. llms/extensions/core_tools/ui/codemirror/lib/codemirror.css +0 -344
  60. llms/extensions/core_tools/ui/codemirror/lib/codemirror.js +0 -9884
  61. llms/extensions/core_tools/ui/codemirror/mode/clike/clike.js +0 -942
  62. llms/extensions/core_tools/ui/codemirror/mode/javascript/index.html +0 -118
  63. llms/extensions/core_tools/ui/codemirror/mode/javascript/javascript.js +0 -962
  64. llms/extensions/core_tools/ui/codemirror/mode/javascript/typescript.html +0 -62
  65. llms/extensions/core_tools/ui/codemirror/mode/python/python.js +0 -402
  66. llms/extensions/core_tools/ui/codemirror/theme/dracula.css +0 -40
  67. llms/extensions/core_tools/ui/codemirror/theme/mocha.css +0 -135
  68. llms/extensions/core_tools/ui/index.mjs +0 -650
  69. llms/extensions/gallery/README.md +0 -61
  70. llms/extensions/gallery/__init__.py +0 -61
  71. llms/extensions/gallery/__pycache__/__init__.cpython-314.pyc +0 -0
  72. llms/extensions/gallery/__pycache__/db.cpython-314.pyc +0 -0
  73. llms/extensions/gallery/db.py +0 -298
  74. llms/extensions/gallery/ui/index.mjs +0 -482
  75. llms/extensions/katex/README.md +0 -39
  76. llms/extensions/katex/__init__.py +0 -6
  77. llms/extensions/katex/__pycache__/__init__.cpython-314.pyc +0 -0
  78. llms/extensions/katex/ui/README.md +0 -125
  79. llms/extensions/katex/ui/contrib/auto-render.js +0 -338
  80. llms/extensions/katex/ui/contrib/auto-render.min.js +0 -1
  81. llms/extensions/katex/ui/contrib/auto-render.mjs +0 -244
  82. llms/extensions/katex/ui/contrib/copy-tex.js +0 -127
  83. llms/extensions/katex/ui/contrib/copy-tex.min.js +0 -1
  84. llms/extensions/katex/ui/contrib/copy-tex.mjs +0 -105
  85. llms/extensions/katex/ui/contrib/mathtex-script-type.js +0 -109
  86. llms/extensions/katex/ui/contrib/mathtex-script-type.min.js +0 -1
  87. llms/extensions/katex/ui/contrib/mathtex-script-type.mjs +0 -24
  88. llms/extensions/katex/ui/contrib/mhchem.js +0 -3213
  89. llms/extensions/katex/ui/contrib/mhchem.min.js +0 -1
  90. llms/extensions/katex/ui/contrib/mhchem.mjs +0 -3109
  91. llms/extensions/katex/ui/contrib/render-a11y-string.js +0 -887
  92. llms/extensions/katex/ui/contrib/render-a11y-string.min.js +0 -1
  93. llms/extensions/katex/ui/contrib/render-a11y-string.mjs +0 -800
  94. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.ttf +0 -0
  95. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff +0 -0
  96. llms/extensions/katex/ui/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  97. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  98. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  99. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  100. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  101. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  102. llms/extensions/katex/ui/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  103. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  104. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  105. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  106. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  107. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  108. llms/extensions/katex/ui/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  109. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.ttf +0 -0
  110. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff +0 -0
  111. llms/extensions/katex/ui/fonts/KaTeX_Main-Bold.woff2 +0 -0
  112. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  113. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  114. llms/extensions/katex/ui/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  115. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.ttf +0 -0
  116. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff +0 -0
  117. llms/extensions/katex/ui/fonts/KaTeX_Main-Italic.woff2 +0 -0
  118. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.ttf +0 -0
  119. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff +0 -0
  120. llms/extensions/katex/ui/fonts/KaTeX_Main-Regular.woff2 +0 -0
  121. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  122. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  123. llms/extensions/katex/ui/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  124. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.ttf +0 -0
  125. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff +0 -0
  126. llms/extensions/katex/ui/fonts/KaTeX_Math-Italic.woff2 +0 -0
  127. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  128. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  129. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  130. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  131. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  132. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  133. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  134. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  135. llms/extensions/katex/ui/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  136. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.ttf +0 -0
  137. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff +0 -0
  138. llms/extensions/katex/ui/fonts/KaTeX_Script-Regular.woff2 +0 -0
  139. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.ttf +0 -0
  140. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff +0 -0
  141. llms/extensions/katex/ui/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  142. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.ttf +0 -0
  143. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff +0 -0
  144. llms/extensions/katex/ui/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  145. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.ttf +0 -0
  146. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff +0 -0
  147. llms/extensions/katex/ui/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  148. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.ttf +0 -0
  149. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff +0 -0
  150. llms/extensions/katex/ui/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  151. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  152. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  153. llms/extensions/katex/ui/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  154. llms/extensions/katex/ui/index.mjs +0 -92
  155. llms/extensions/katex/ui/katex-swap.css +0 -1230
  156. llms/extensions/katex/ui/katex-swap.min.css +0 -1
  157. llms/extensions/katex/ui/katex.css +0 -1230
  158. llms/extensions/katex/ui/katex.js +0 -19080
  159. llms/extensions/katex/ui/katex.min.css +0 -1
  160. llms/extensions/katex/ui/katex.min.js +0 -1
  161. llms/extensions/katex/ui/katex.min.mjs +0 -1
  162. llms/extensions/katex/ui/katex.mjs +0 -18547
  163. llms/extensions/providers/__init__.py +0 -18
  164. llms/extensions/providers/__pycache__/__init__.cpython-314.pyc +0 -0
  165. llms/extensions/providers/__pycache__/anthropic.cpython-314.pyc +0 -0
  166. llms/extensions/providers/__pycache__/chutes.cpython-314.pyc +0 -0
  167. llms/extensions/providers/__pycache__/google.cpython-314.pyc +0 -0
  168. llms/extensions/providers/__pycache__/nvidia.cpython-314.pyc +0 -0
  169. llms/extensions/providers/__pycache__/openai.cpython-314.pyc +0 -0
  170. llms/extensions/providers/__pycache__/openrouter.cpython-314.pyc +0 -0
  171. llms/extensions/providers/anthropic.py +0 -229
  172. llms/extensions/providers/chutes.py +0 -155
  173. llms/extensions/providers/google.py +0 -378
  174. llms/extensions/providers/nvidia.py +0 -105
  175. llms/extensions/providers/openai.py +0 -156
  176. llms/extensions/providers/openrouter.py +0 -72
  177. llms/extensions/system_prompts/README.md +0 -22
  178. llms/extensions/system_prompts/__init__.py +0 -45
  179. llms/extensions/system_prompts/__pycache__/__init__.cpython-314.pyc +0 -0
  180. llms/extensions/system_prompts/ui/index.mjs +0 -280
  181. llms/extensions/system_prompts/ui/prompts.json +0 -1067
  182. llms/extensions/tools/__init__.py +0 -5
  183. llms/extensions/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  184. llms/extensions/tools/ui/index.mjs +0 -204
  185. llms/providers-extra.json +0 -356
  186. llms/ui/ctx.mjs +0 -365
  187. llms/ui/modules/chat/ChatBody.mjs +0 -691
  188. llms/ui/modules/chat/index.mjs +0 -828
  189. llms/ui/modules/layout.mjs +0 -243
  190. llms/ui/modules/model-selector.mjs +0 -851
  191. llms_py-3.0.0.dist-info/RECORD +0 -202
  192. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/WHEEL +0 -0
  193. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/entry_points.txt +0 -0
  194. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/licenses/LICENSE +0 -0
  195. {llms_py-3.0.0.dist-info → llms_py-3.0.0b2.dist-info}/top_level.txt +0 -0
@@ -1,72 +0,0 @@
1
- import json
2
- import time
3
-
4
- import aiohttp
5
-
6
-
7
- def install_openrouter(ctx):
8
- from llms.main import GeneratorBase
9
-
10
- # https://openrouter.ai/docs/guides/overview/multimodal/image-generation
11
- class OpenRouterGenerator(GeneratorBase):
12
- sdk = "openrouter/image"
13
-
14
- def __init__(self, **kwargs):
15
- super().__init__(**kwargs)
16
-
17
- def to_response(self, response, chat, started_at):
18
- # go through all image responses and save them to cache
19
- cost = None
20
- if "usage" in response and "cost" in response["usage"]:
21
- cost = response["usage"]["cost"]
22
- for choice in response["choices"]:
23
- if "message" in choice and "images" in choice["message"]:
24
- for image in choice["message"]["images"]:
25
- if choice["message"]["content"] == "":
26
- choice["message"]["content"] = self.default_content
27
- if "image_url" in image:
28
- data_uri = image["image_url"]["url"]
29
- if data_uri.startswith("data:"):
30
- parts = data_uri.split(",", 1)
31
- ext = parts[0].split(";")[0].split("/")[1]
32
- base64_data = parts[1]
33
- model = chat["model"].split("/")[-1]
34
- filename = f"{model}-{choice['index']}.{ext}"
35
- relative_url, info = ctx.save_image_to_cache(
36
- base64_data, filename, ctx.to_file_info(chat, {"cost": cost})
37
- )
38
- image["image_url"]["url"] = relative_url
39
-
40
- return response
41
-
42
- async def chat(self, chat, provider=None):
43
- headers = self.get_headers(provider, chat)
44
- if provider is not None:
45
- chat["model"] = provider.provider_model(chat["model"]) or chat["model"]
46
-
47
- started_at = time.time()
48
- if ctx.MOCK:
49
- print("Mocking OpenRouterGenerator")
50
- text = ctx.text_from_file(f"{ctx.MOCK_DIR}/openrouter-image.json")
51
- return ctx.log_json(self.to_response(json.loads(text), chat, started_at))
52
- else:
53
- chat_url = provider.chat_url
54
- # remove tools
55
- chat.pop("tools", None)
56
- chat = await self.process_chat(chat, provider_id=self.id)
57
- ctx.log(f"POST {chat_url}")
58
- ctx.log(provider.chat_summary(chat))
59
- # remove metadata if any (conflicts with some providers, e.g. Z.ai)
60
- metadata = chat.pop("metadata", None)
61
-
62
- async with aiohttp.ClientSession() as session, session.post(
63
- chat_url,
64
- headers=headers,
65
- data=json.dumps(chat),
66
- timeout=aiohttp.ClientTimeout(total=300),
67
- ) as response:
68
- if metadata:
69
- chat["metadata"] = metadata
70
- return ctx.log_json(self.to_response(await self.response_json(response), chat, started_at))
71
-
72
- ctx.add_provider(OpenRouterGenerator)
@@ -1,22 +0,0 @@
1
- # System Prompts Extension
2
-
3
- This extension configures AI requests with a library of **over 200+** awesome curated system prompts that can be selected from the UI.
4
-
5
- ## Custom System Prompts
6
-
7
- You can also maintain your own library of system prompts which can be maintained for all anonymous users at:
8
- `~/.llms/user/default/system-prompts.json`
9
-
10
- Or for signed in users at:
11
- `~/.llms/user/<github-user>/system-prompts.json`
12
-
13
- The JSON file should contain an array of Prompt objects, e.g:
14
-
15
- ```json
16
- [
17
- {
18
- "name": "Helpful Assistant",
19
- "prompt": "You are a helpful assistant."
20
- }
21
- ]
22
- ```
@@ -1,45 +0,0 @@
1
- import json
2
- import os
3
-
4
- from aiohttp import web
5
-
6
- default_prompts = [
7
- {"name": "Helpful Assistant", "prompt": "You are a helpful assistant."},
8
- ]
9
-
10
-
11
- # runs after providers are configured but before server is run
12
- def install(ctx):
13
- # helper to get user or default prompts
14
- def get_user_prompts(request):
15
- candidate_paths = []
16
- # check if user is signed in
17
- username = ctx.get_username(request)
18
- if username:
19
- # if signed in (Github OAuth), return the prompts for this user if exists
20
- candidate_paths.append(os.path.join(ctx.get_user_path(username), "system_prompts", "prompts.json"))
21
- # return default prompts for all users if exists
22
- candidate_paths.append(os.path.join(ctx.get_user_path(), "system_prompts", "prompts.json"))
23
- # otherwise return the default prompts from this repo
24
- candidate_paths.append(os.path.join(ctx.path, "ui", "prompts.json"))
25
-
26
- # iterate all candidate paths and when exists return its json
27
- for path in candidate_paths:
28
- if os.path.exists(path):
29
- with open(path, encoding="utf-8") as f:
30
- txt = f.read()
31
- return json.loads(txt)
32
- return default_prompts
33
-
34
- # API Handler to get prompts
35
- async def get_prompts(request):
36
- prompts_json = get_user_prompts(request)
37
- return web.json_response(prompts_json)
38
-
39
- ctx.add_get("prompts.json", get_prompts)
40
-
41
-
42
- # register install extension handler
43
- __install__ = install
44
-
45
- __order__ = -10
@@ -1,280 +0,0 @@
1
- import { ref, computed, inject, watch, onMounted, onUnmounted, nextTick } from "vue"
2
- import { AppContext } from "ctx.mjs"
3
-
4
- let ext
5
-
6
- const PromptFinder = {
7
- template: `
8
- <div v-if="modelValue" class="absolute right-0 top-full z-10 mt-1 origin-top-right rounded-md bg-white dark:bg-gray-900 shadow-lg border border-gray-300 dark:border-gray-600 focus:outline-none"
9
- style="width:400px"
10
- role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
11
- <div class="p-2" role="none">
12
- <div class="relative mb-2">
13
- <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
14
- <svg class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
15
- <path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
16
- </svg>
17
- </div>
18
- <input type="text"
19
- ref="searchInput"
20
- v-model="searchQuery"
21
- @keydown="onKeydown"
22
- class="block w-full rounded-md border-0 py-1.5 pl-10 text-gray-900 dark:text-gray-100 shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-gray-600 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-600 sm:text-xs sm:leading-6 bg-transparent"
23
- placeholder="Search prompts...">
24
- </div>
25
-
26
- <div class="max-h-80 overflow-y-auto" ref="resultsList">
27
- <div v-if="filteredPrompts.length === 0" class="p-4 text-center text-xs text-gray-500">
28
- No prompts found
29
- </div>
30
- <div v-for="(prompt, index) in filteredPrompts" :key="prompt.id"
31
- @click="selectPrompt(prompt)"
32
- :class="['group relative flex gap-x-2 rounded-md p-2 cursor-pointer border-b border-gray-100 dark:border-gray-800 last:border-0',
33
- selectedIndex === index ? 'bg-blue-50 dark:bg-blue-900/20' : 'hover:bg-gray-50 dark:hover:bg-gray-800']"
34
- :data-index="index">
35
- <div class="flex-auto">
36
- <div class="flex items-center justify-between">
37
- <h4 :class="['font-semibold text-sm', selectedIndex === index ? 'text-blue-700 dark:text-blue-300' : 'text-gray-900 dark:text-gray-100']">
38
- {{ prompt.name }}
39
- </h4>
40
- </div>
41
- <p class="text-xs leading-4 text-gray-500 dark:text-gray-400 line-clamp-2 mt-0.5">{{ prompt.value }}</p>
42
- </div>
43
- </div>
44
- </div>
45
- </div>
46
- </div>
47
- `,
48
- props: {
49
- modelValue: Boolean, // controls visibility
50
- prompts: {
51
- type: Array,
52
- default: () => []
53
- }
54
- },
55
- emits: ['update:modelValue', 'select'],
56
- setup(props, { emit }) {
57
- const searchQuery = ref('')
58
- const searchInput = ref(null)
59
- const resultsList = ref(null)
60
- const selectedIndex = ref(-1)
61
-
62
- const filteredPrompts = computed(() => {
63
- if (!searchQuery.value) return props.prompts
64
- const q = searchQuery.value.toLowerCase()
65
- return props.prompts.filter(p =>
66
- p.name.toLowerCase().includes(q) ||
67
- p.value.toLowerCase().includes(q) ||
68
- p.id.toLowerCase().includes(q)
69
- )
70
- })
71
-
72
- function selectPrompt(prompt) {
73
- emit('select', prompt)
74
- emit('update:modelValue', false)
75
- }
76
-
77
- function scrollToSelected() {
78
- nextTick(() => {
79
- if (!resultsList.value) return
80
- const el = resultsList.value.querySelector(`[data-index="${selectedIndex.value}"]`)
81
- if (el) {
82
- el.scrollIntoView({ block: 'nearest' })
83
- }
84
- })
85
- }
86
-
87
- function onKeydown(e) {
88
- if (filteredPrompts.value.length === 0) return
89
-
90
- if (e.key === 'ArrowDown') {
91
- e.preventDefault()
92
- selectedIndex.value = (selectedIndex.value + 1) % filteredPrompts.value.length
93
- scrollToSelected()
94
- } else if (e.key === 'ArrowUp') {
95
- e.preventDefault()
96
- selectedIndex.value = (selectedIndex.value - 1 + filteredPrompts.value.length) % filteredPrompts.value.length
97
- scrollToSelected()
98
- } else if (e.key === 'Enter') {
99
- e.preventDefault()
100
- if (selectedIndex.value >= 0 && selectedIndex.value < filteredPrompts.value.length) {
101
- selectPrompt(filteredPrompts.value[selectedIndex.value])
102
- }
103
- }
104
- }
105
-
106
- watch(() => props.modelValue, (isOpen) => {
107
- if (isOpen) {
108
- // Focus search input when modal opens
109
- nextTick(() => {
110
- if (searchInput.value) {
111
- searchInput.value.focus()
112
- }
113
- })
114
- selectedIndex.value = -1
115
- } else {
116
- searchQuery.value = ''
117
- }
118
- })
119
-
120
- watch(searchQuery, () => {
121
- selectedIndex.value = 0 // Select first result on search
122
- })
123
-
124
- return {
125
- searchQuery,
126
- searchInput,
127
- resultsList,
128
- filteredPrompts,
129
- selectedIndex,
130
- selectPrompt,
131
- onKeydown
132
- }
133
- }
134
- }
135
-
136
- const SystemPromptEditor = {
137
- template: `
138
- <div class="border-b border-gray-200 dark:border-gray-700 px-6 pb-4">
139
- <div class="max-w-6xl mx-auto">
140
- <div class="mt-2 h-10 flex justify-between items-center">
141
- <label class="select-none block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
142
- System Prompt
143
- </label>
144
- <div v-if="hasMessages" class="text-sm text-gray-500 dark:text-gray-400">
145
- {{ !ext.prefs.systemPrompt ? '' : prompts.find(x => x.value === ext.prefs.systemPrompt)?.name || 'Custom' }}
146
- </div>
147
- <div v-else class="mb-2 relative" ref="containerRef">
148
- <div class="flex items-center gap-2">
149
- <span v-if="selected" class="text-sm text-gray-500 dark:text-gray-400">
150
- {{ selected.name }}
151
- </span>
152
- <button v-if="modelValue" type="button" title="Clear System Prompt" @click="$emit('update:modelValue', null)"
153
- class="rounded-full p-1 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors">
154
- <svg class="size-4 text-gray-500 dark:text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z"/></svg>
155
- </button>
156
- <button type="button"
157
- @click="ext.setPrefs({ showFinder: !ext.prefs.showFinder })"
158
- class="inline-flex items-center gap-x-1.5 rounded-md bg-white dark:bg-gray-900 px-2.5 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-300 shadow-sm border border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-800">
159
- Explore Prompts
160
- </button>
161
- </div>
162
- <PromptFinder v-model="ext.prefs.showFinder" :prompts="prompts" @select="onSelect" />
163
- </div>
164
- </div>
165
- <div v-if="hasMessages" class="w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2 text-sm">
166
- {{$threads.currentThread.value?.systemPrompt || 'No System Prompt was used' }}
167
- </div>
168
- <div v-else>
169
- <textarea
170
- :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"
171
- placeholder="Enter a system prompt to guide AI's behavior..."
172
- rows="6"
173
- class="block w-full resize-vertical rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2 text-sm placeholder-gray-500 dark:placeholder-gray-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
174
- ></textarea>
175
- </div>
176
- </div>
177
- </div>
178
- `,
179
- emits: ['update:modelValue'],
180
- props: {
181
- prompts: Array,
182
- selected: Object,
183
- modelValue: String,
184
- },
185
- setup(props, { emit }) {
186
- /**@type {AppContext} */
187
- const ctx = inject('ctx')
188
- const containerRef = ref()
189
- const hasMessages = computed(() => ctx.threads.currentThread.value?.messages?.length > 0)
190
- const selected = computed(() =>
191
- props.prompts.find(x => x.value === props.modelValue) ?? { name: "Custom", value: props.modelValue })
192
-
193
- function onSelect(prompt) {
194
- ext.setPrefs({ prompt: prompt }) // {"id","name","value"}
195
- emit('update:modelValue', prompt.value)
196
- }
197
-
198
- function closeFinder(e) {
199
- if (ext.prefs.showFinder && containerRef.value && !containerRef.value.contains(e.target)) {
200
- ext.setPrefs({ showFinder: false })
201
- }
202
- }
203
-
204
- watch(() => props.modelValue, systemPrompt => {
205
- ext.setPrefs({ systemPrompt })
206
- })
207
-
208
- onMounted(() => {
209
- document.addEventListener('click', closeFinder)
210
- if (ext.prefs.prompt) {
211
- emit('update:modelValue', ext.prefs.prompt)
212
- }
213
- })
214
- onUnmounted(() => {
215
- document.removeEventListener('click', closeFinder)
216
- })
217
-
218
- return {
219
- ext,
220
- hasMessages,
221
- selected,
222
- containerRef,
223
- onSelect,
224
- }
225
- }
226
- }
227
-
228
- export default {
229
- order: 30 - 100,
230
-
231
- install(ctx) {
232
- ext = ctx.scope('system_prompts')
233
- ctx.components({
234
- PromptFinder,
235
- SystemPromptEditor,
236
- SystemPromptsPanel: {
237
- template: `<SystemPromptEditor :prompts="ext.state.prompts" v-model="ext.prefs.prompt" />`,
238
- setup() {
239
- return { ext }
240
- }
241
- }
242
- })
243
- ext.setPrefs({ systemPrompt: '' })
244
-
245
- ctx.setTopIcons({
246
- system_prompts: {
247
- component: {
248
- template: `<svg @click="$ctx.toggleTop('SystemPromptsPanel')" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m5 7l5 5l-5 5m8 0h6"/></svg>`,
249
- },
250
- isActive({ top }) { return top === 'SystemPromptsPanel' }
251
- }
252
- })
253
-
254
- ctx.chatRequestFilters.push(({ request, thread }) => {
255
-
256
- const hasSystemPrompt = request.messages.find(x => x.role === 'system')
257
- if (hasSystemPrompt) {
258
- console.log('Already has system prompt', hasSystemPrompt.content)
259
- return
260
- }
261
-
262
- // Only add the selected system prompt for new requests
263
- if (ext.prefs.systemPrompt && request.messages.length <= 1) {
264
- // add message to start
265
- request.messages.unshift({
266
- role: 'system',
267
- content: ext.prefs.systemPrompt
268
- })
269
- }
270
- })
271
-
272
- ctx.setState({ prompts: [] })
273
- },
274
-
275
- async load(ctx) {
276
- const api = await ext.getJson(`/prompts.json`)
277
- const prompts = api.response || []
278
- ext.setState({ prompts })
279
- }
280
- }