You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
5.9 KiB

11 months ago
  1. const ImportedPlugin = require("../utils/agents/imported");
  2. /**
  3. * An interface to the AnythingLLM Community Hub external API.
  4. */
  5. const CommunityHub = {
  6. importPrefix: "allm-community-id",
  7. apiBase:
  8. process.env.NODE_ENV === "development"
  9. ? "http://127.0.0.1:5001/anythingllm-hub/us-central1/external/v1"
  10. : "https://hub.external.anythingllm.com/v1",
  11. /**
  12. * Validate an import ID and return the entity type and ID.
  13. * @param {string} importId - The import ID to validate.
  14. * @returns {{entityType: string | null, entityId: string | null}}
  15. */
  16. validateImportId: function (importId) {
  17. if (
  18. !importId ||
  19. !importId.startsWith(this.importPrefix) ||
  20. importId.split(":").length !== 3
  21. )
  22. return { entityType: null, entityId: null };
  23. const [_, entityType, entityId] = importId.split(":");
  24. if (!entityType || !entityId) return { entityType: null, entityId: null };
  25. return {
  26. entityType: String(entityType).trim(),
  27. entityId: String(entityId).trim(),
  28. };
  29. },
  30. /**
  31. * Fetch the explore items from the community hub that are publicly available.
  32. * @returns {Promise<{agentSkills: {items: [], hasMore: boolean, totalCount: number}, systemPrompts: {items: [], hasMore: boolean, totalCount: number}, slashCommands: {items: [], hasMore: boolean, totalCount: number}}>}
  33. */
  34. fetchExploreItems: async function () {
  35. return await fetch(`${this.apiBase}/explore`, {
  36. method: "GET",
  37. })
  38. .then((response) => response.json())
  39. .catch((error) => {
  40. console.error("Error fetching explore items:", error);
  41. return {
  42. agentSkills: {
  43. items: [],
  44. hasMore: false,
  45. totalCount: 0,
  46. },
  47. systemPrompts: {
  48. items: [],
  49. hasMore: false,
  50. totalCount: 0,
  51. },
  52. slashCommands: {
  53. items: [],
  54. hasMore: false,
  55. totalCount: 0,
  56. },
  57. };
  58. });
  59. },
  60. /**
  61. * Fetch a bundle item from the community hub.
  62. * Bundle items are entities that require a downloadURL to be fetched from the community hub.
  63. * so we can unzip and import them to the AnythingLLM instance.
  64. * @param {string} importId - The import ID of the item.
  65. * @returns {Promise<{url: string | null, item: object | null, error: string | null}>}
  66. */
  67. getBundleItem: async function (importId) {
  68. const { entityType, entityId } = this.validateImportId(importId);
  69. if (!entityType || !entityId)
  70. return { item: null, error: "Invalid import ID" };
  71. const { SystemSettings } = require("./systemSettings");
  72. const { connectionKey } = await SystemSettings.hubSettings();
  73. const { url, item, error } = await fetch(
  74. `${this.apiBase}/${entityType}/${entityId}/pull`,
  75. {
  76. method: "GET",
  77. headers: {
  78. "Content-Type": "application/json",
  79. ...(connectionKey
  80. ? { Authorization: `Bearer ${connectionKey}` }
  81. : {}),
  82. },
  83. }
  84. )
  85. .then((response) => response.json())
  86. .catch((error) => {
  87. console.error(
  88. `Error fetching bundle item for import ID ${importId}:`,
  89. error
  90. );
  91. return { url: null, item: null, error: error.message };
  92. });
  93. return { url, item, error };
  94. },
  95. /**
  96. * Apply an item to the AnythingLLM instance. Used for simple items like slash commands and system prompts.
  97. * @param {object} item - The item to apply.
  98. * @param {object} options - Additional options for applying the item.
  99. * @param {object|null} options.currentUser - The current user object.
  100. * @returns {Promise<{success: boolean, error: string | null}>}
  101. */
  102. applyItem: async function (item, options = {}) {
  103. if (!item) return { success: false, error: "Item is required" };
  104. if (item.itemType === "system-prompt") {
  105. if (!options?.workspaceSlug)
  106. return { success: false, error: "Workspace slug is required" };
  107. const { Workspace } = require("./workspace");
  108. const workspace = await Workspace.get({
  109. slug: String(options.workspaceSlug),
  110. });
  111. if (!workspace) return { success: false, error: "Workspace not found" };
  112. await Workspace.update(workspace.id, { openAiPrompt: item.prompt });
  113. return { success: true, error: null };
  114. }
  115. if (item.itemType === "slash-command") {
  116. const { SlashCommandPresets } = require("./slashCommandsPresets");
  117. await SlashCommandPresets.create(options?.currentUser?.id, {
  118. command: SlashCommandPresets.formatCommand(String(item.command)),
  119. prompt: String(item.prompt),
  120. description: String(item.description),
  121. });
  122. return { success: true, error: null };
  123. }
  124. return {
  125. success: false,
  126. error: "Unsupported item type. Nothing to apply.",
  127. };
  128. },
  129. /**
  130. * Import a bundle item to the AnythingLLM instance by downloading the zip file and importing it.
  131. * or whatever the item type requires.
  132. * @param {{url: string, item: object}} params
  133. * @returns {Promise<{success: boolean, error: string | null}>}
  134. */
  135. importBundleItem: async function ({ url, item }) {
  136. if (item.itemType === "agent-skill") {
  137. const { success, error } =
  138. await ImportedPlugin.importCommunityItemFromUrl(url, item);
  139. return { success, error };
  140. }
  141. return {
  142. success: false,
  143. error: "Unsupported item type. Nothing to import.",
  144. };
  145. },
  146. fetchUserItems: async function (connectionKey) {
  147. if (!connectionKey) return { createdByMe: {}, teamItems: [] };
  148. return await fetch(`${this.apiBase}/items`, {
  149. method: "GET",
  150. headers: {
  151. "Content-Type": "application/json",
  152. Authorization: `Bearer ${connectionKey}`,
  153. },
  154. })
  155. .then((response) => response.json())
  156. .catch((error) => {
  157. console.error("Error fetching user items:", error);
  158. return { createdByMe: {}, teamItems: [] };
  159. });
  160. },
  161. };
  162. module.exports = { CommunityHub };