prompts_router.py 15 KB

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