prompts_router.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. import textwrap
  2. from typing import Optional
  3. from fastapi import Body, Depends, Path, Query
  4. from core.base import R2RException
  5. from core.base.api.models import (
  6. GenericBooleanResponse,
  7. GenericMessageResponse,
  8. WrappedBooleanResponse,
  9. WrappedGenericMessageResponse,
  10. WrappedPromptResponse,
  11. WrappedPromptsResponse,
  12. )
  13. from ...abstractions import R2RProviders, R2RServices
  14. from .base_router import BaseRouterV3
  15. class PromptsRouter(BaseRouterV3):
  16. def __init__(
  17. self,
  18. providers: R2RProviders,
  19. services: R2RServices,
  20. ):
  21. super().__init__(providers, services)
  22. def _setup_routes(self):
  23. @self.router.post(
  24. "/prompts",
  25. dependencies=[Depends(self.rate_limit_dependency)],
  26. summary="Create a new prompt",
  27. openapi_extra={
  28. "x-codeSamples": [
  29. {
  30. "lang": "Python",
  31. "source": textwrap.dedent(
  32. """
  33. from r2r import R2RClient
  34. client = R2RClient()
  35. # when using auth, do client.login(...)
  36. result = client.prompts.create(
  37. name="greeting_prompt",
  38. template="Hello, {name}!",
  39. input_types={"name": "string"}
  40. )
  41. """
  42. ),
  43. },
  44. {
  45. "lang": "JavaScript",
  46. "source": textwrap.dedent(
  47. """
  48. const { r2rClient } = require("r2r-js");
  49. const client = new r2rClient();
  50. function main() {
  51. const response = await client.prompts.create({
  52. name: "greeting_prompt",
  53. template: "Hello, {name}!",
  54. inputTypes: { name: "string" },
  55. });
  56. }
  57. main();
  58. """
  59. ),
  60. },
  61. {
  62. "lang": "cURL",
  63. "source": textwrap.dedent(
  64. """
  65. curl -X POST "https://api.example.com/v3/prompts" \\
  66. -H "Authorization: Bearer YOUR_API_KEY" \\
  67. -H "Content-Type: application/json" \\
  68. -d '{"name": "greeting_prompt", "template": "Hello, {name}!", "input_types": {"name": "string"}}'
  69. """
  70. ),
  71. },
  72. ]
  73. },
  74. )
  75. @self.base_endpoint
  76. async def create_prompt(
  77. name: str = Body(..., description="The name of the prompt"),
  78. template: str = Body(
  79. ..., description="The template string for the prompt"
  80. ),
  81. input_types: dict[str, str] = Body(
  82. default={},
  83. description="A dictionary mapping input names to their types",
  84. ),
  85. auth_user=Depends(self.providers.auth.auth_wrapper()),
  86. ) -> WrappedGenericMessageResponse:
  87. """
  88. Create a new prompt with the given configuration.
  89. This endpoint allows superusers to create a new prompt with a specified name, template, and input types.
  90. """
  91. if not auth_user.is_superuser:
  92. raise R2RException(
  93. "Only a superuser can create prompts.",
  94. 403,
  95. )
  96. result = await self.services.management.add_prompt(
  97. name, template, input_types
  98. )
  99. return GenericMessageResponse(message=result) # type: ignore
  100. @self.router.get(
  101. "/prompts",
  102. dependencies=[Depends(self.rate_limit_dependency)],
  103. summary="List all prompts",
  104. openapi_extra={
  105. "x-codeSamples": [
  106. {
  107. "lang": "Python",
  108. "source": textwrap.dedent(
  109. """
  110. from r2r import R2RClient
  111. client = R2RClient()
  112. # when using auth, do client.login(...)
  113. result = client.prompts.list()
  114. """
  115. ),
  116. },
  117. {
  118. "lang": "JavaScript",
  119. "source": textwrap.dedent(
  120. """
  121. const { r2rClient } = require("r2r-js");
  122. const client = new r2rClient();
  123. function main() {
  124. const response = await client.prompts.list();
  125. }
  126. main();
  127. """
  128. ),
  129. },
  130. {
  131. "lang": "CLI",
  132. "source": textwrap.dedent(
  133. """
  134. r2r prompts list
  135. """
  136. ),
  137. },
  138. {
  139. "lang": "cURL",
  140. "source": textwrap.dedent(
  141. """
  142. curl -X GET "https://api.example.com/v3/prompts" \\
  143. -H "Authorization: Bearer YOUR_API_KEY"
  144. """
  145. ),
  146. },
  147. ]
  148. },
  149. )
  150. @self.base_endpoint
  151. async def get_prompts(
  152. auth_user=Depends(self.providers.auth.auth_wrapper()),
  153. ) -> WrappedPromptsResponse:
  154. """
  155. List all available prompts.
  156. This endpoint retrieves a list of all prompts in the system. Only superusers can access this endpoint.
  157. """
  158. if not auth_user.is_superuser:
  159. raise R2RException(
  160. "Only a superuser can list prompts.",
  161. 403,
  162. )
  163. get_prompts_response = (
  164. await self.services.management.get_all_prompts()
  165. )
  166. return ( # type: ignore
  167. get_prompts_response["results"],
  168. {
  169. "total_entries": get_prompts_response["total_entries"],
  170. },
  171. )
  172. @self.router.post(
  173. "/prompts/{name}",
  174. dependencies=[Depends(self.rate_limit_dependency)],
  175. summary="Get a specific prompt",
  176. openapi_extra={
  177. "x-codeSamples": [
  178. {
  179. "lang": "Python",
  180. "source": textwrap.dedent(
  181. """
  182. from r2r import R2RClient
  183. client = R2RClient()
  184. # when using auth, do client.login(...)
  185. result = client.prompts.get(
  186. "greeting_prompt",
  187. inputs={"name": "John"},
  188. prompt_override="Hi, {name}!"
  189. )
  190. """
  191. ),
  192. },
  193. {
  194. "lang": "JavaScript",
  195. "source": textwrap.dedent(
  196. """
  197. const { r2rClient } = require("r2r-js");
  198. const client = new r2rClient();
  199. function main() {
  200. const response = await client.prompts.retrieve({
  201. name: "greeting_prompt",
  202. inputs: { name: "John" },
  203. promptOverride: "Hi, {name}!",
  204. });
  205. }
  206. main();
  207. """
  208. ),
  209. },
  210. {
  211. "lang": "CLI",
  212. "source": textwrap.dedent(
  213. """
  214. r2r prompts retrieve greeting_prompt --inputs '{"name": "John"}' --prompt-override "Hi, {name}!"
  215. """
  216. ),
  217. },
  218. {
  219. "lang": "cURL",
  220. "source": textwrap.dedent(
  221. """
  222. curl -X POST "https://api.example.com/v3/prompts/greeting_prompt?inputs=%7B%22name%22%3A%22John%22%7D&prompt_override=Hi%2C%20%7Bname%7D!" \\
  223. -H "Authorization: Bearer YOUR_API_KEY"
  224. """
  225. ),
  226. },
  227. ]
  228. },
  229. )
  230. @self.base_endpoint
  231. async def get_prompt(
  232. name: str = Path(..., description="Prompt name"),
  233. inputs: Optional[dict[str, str]] = Body(
  234. None, description="Prompt inputs"
  235. ),
  236. prompt_override: Optional[str] = Query(
  237. None, description="Prompt override"
  238. ),
  239. auth_user=Depends(self.providers.auth.auth_wrapper()),
  240. ) -> WrappedPromptResponse:
  241. """
  242. Get a specific prompt by name, optionally with inputs and override.
  243. This endpoint retrieves a specific prompt and allows for optional inputs and template override.
  244. Only superusers can access this endpoint.
  245. """
  246. if not auth_user.is_superuser:
  247. raise R2RException(
  248. "Only a superuser can retrieve prompts.",
  249. 403,
  250. )
  251. result = await self.services.management.get_prompt(
  252. name, inputs, prompt_override
  253. )
  254. return result # type: ignore
  255. @self.router.put(
  256. "/prompts/{name}",
  257. dependencies=[Depends(self.rate_limit_dependency)],
  258. summary="Update an existing prompt",
  259. openapi_extra={
  260. "x-codeSamples": [
  261. {
  262. "lang": "Python",
  263. "source": textwrap.dedent(
  264. """
  265. from r2r import R2RClient
  266. client = R2RClient()
  267. # when using auth, do client.login(...)
  268. result = client.prompts.update(
  269. "greeting_prompt",
  270. template="Greetings, {name}!",
  271. input_types={"name": "string", "age": "integer"}
  272. )
  273. """
  274. ),
  275. },
  276. {
  277. "lang": "JavaScript",
  278. "source": textwrap.dedent(
  279. """
  280. const { r2rClient } = require("r2r-js");
  281. const client = new r2rClient();
  282. function main() {
  283. const response = await client.prompts.update({
  284. name: "greeting_prompt",
  285. template: "Greetings, {name}!",
  286. inputTypes: { name: "string", age: "integer" },
  287. });
  288. }
  289. main();
  290. """
  291. ),
  292. },
  293. {
  294. "lang": "cURL",
  295. "source": textwrap.dedent(
  296. """
  297. curl -X PUT "https://api.example.com/v3/prompts/greeting_prompt" \\
  298. -H "Authorization: Bearer YOUR_API_KEY" \\
  299. -H "Content-Type: application/json" \\
  300. -d '{"template": "Greetings, {name}!", "input_types": {"name": "string", "age": "integer"}}'
  301. """
  302. ),
  303. },
  304. ]
  305. },
  306. )
  307. @self.base_endpoint
  308. async def update_prompt(
  309. name: str = Path(..., description="Prompt name"),
  310. template: Optional[str] = Body(
  311. None, description="Updated prompt template"
  312. ),
  313. input_types: dict[str, str] = Body(
  314. default={},
  315. description="A dictionary mapping input names to their types",
  316. ),
  317. auth_user=Depends(self.providers.auth.auth_wrapper()),
  318. ) -> WrappedGenericMessageResponse:
  319. """
  320. Update an existing prompt's template and/or input types.
  321. This endpoint allows superusers to update the template and input types of an existing prompt.
  322. """
  323. if not auth_user.is_superuser:
  324. raise R2RException(
  325. "Only a superuser can update prompts.",
  326. 403,
  327. )
  328. result = await self.services.management.update_prompt(
  329. name, template, input_types
  330. )
  331. return GenericMessageResponse(message=result) # type: ignore
  332. @self.router.delete(
  333. "/prompts/{name}",
  334. dependencies=[Depends(self.rate_limit_dependency)],
  335. summary="Delete a prompt",
  336. openapi_extra={
  337. "x-codeSamples": [
  338. {
  339. "lang": "Python",
  340. "source": textwrap.dedent(
  341. """
  342. from r2r import R2RClient
  343. client = R2RClient()
  344. # when using auth, do client.login(...)
  345. result = client.prompts.delete("greeting_prompt")
  346. """
  347. ),
  348. },
  349. {
  350. "lang": "JavaScript",
  351. "source": textwrap.dedent(
  352. """
  353. const { r2rClient } = require("r2r-js");
  354. const client = new r2rClient();
  355. function main() {
  356. const response = await client.prompts.delete({
  357. name: "greeting_prompt",
  358. });
  359. }
  360. main();
  361. """
  362. ),
  363. },
  364. {
  365. "lang": "CLI",
  366. "source": textwrap.dedent(
  367. """
  368. r2r prompts delete greeting_prompt
  369. """
  370. ),
  371. },
  372. {
  373. "lang": "cURL",
  374. "source": textwrap.dedent(
  375. """
  376. curl -X DELETE "https://api.example.com/v3/prompts/greeting_prompt" \\
  377. -H "Authorization: Bearer YOUR_API_KEY"
  378. """
  379. ),
  380. },
  381. ]
  382. },
  383. )
  384. @self.base_endpoint
  385. async def delete_prompt(
  386. name: str = Path(..., description="Prompt name"),
  387. auth_user=Depends(self.providers.auth.auth_wrapper()),
  388. ) -> WrappedBooleanResponse:
  389. """
  390. Delete a prompt by name.
  391. This endpoint allows superusers to delete an existing prompt.
  392. """
  393. if not auth_user.is_superuser:
  394. raise R2RException(
  395. "Only a superuser can delete prompts.",
  396. 403,
  397. )
  398. await self.services.management.delete_prompt(name)
  399. return GenericBooleanResponse(success=True) # type: ignore