diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ce090..0258e56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,13 @@ All notable changes to this project are documented here. The format follows for an account, and `resubscribeWebhook` puts a lapsed subscription back. - `inbox().passThreadControl`, `takeThreadControl` and `handover` move a Messenger thread between Meta apps (`inbox` scope, plus `publish`). +- `knowledge()`: the workspace knowledge base — `list`, `create` (plus the + `createText`, `createUrl` and `createFile` shorthands), `update`, `delete`, + `sync` and `search`, with the `KnowledgeSource` and `KnowledgeMatch` records. + A source is an FAQ, a note, a URL on your own site or a plain-text/CSV media + item; `search` returns the passages closest to a question, and is what grounds + a drafted inbox reply in your own answers. Needs the `inbox` scope. + - `accounts()`: `listSlackChannels`, `listSlackMembers`, `getSlackIdentity` and `updateSlackIdentity` (`UpdateSlackIdentityParams`) for a Slack account. A webhook-connected account answers 409 `webhook_connection`. diff --git a/README.md b/README.md index a4c54ca..799b5fc 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,8 @@ long failed = client.posts().stream(PostListParams.create().workspaceId(workspac | `media()` | `list`, `upload`, `presign`, `complete`, `uploadDirect`, `delete` | | `ai()` | `credits`, `generateCaption`, `rewrite`, `repurposeUrl` | | `inbox()` | `list`, `threads`, `conversations`, `unreadCount`, `accounts`, `platforms`, `markThreadRead`, `markConversationRead`, `refresh`, `update`, `editComment`, `reply`, `hide`, `unhide`, `delete`, `like`, `unlike`, `pin`, `unpin`, `react`, `startConversation`, `setTyping`, `passThreadControl`, `takeThreadControl`, `handover`, `listApprovals`, `approveReply`, `rejectReply` | +| `inbox()` | `list`, `threads`, `conversations`, `unreadCount`, `accounts`, `platforms`, `markThreadRead`, `markConversationRead`, `refresh`, `update`, `editComment`, `reply`, `hide`, `unhide`, `delete`, `like`, `unlike`, `pin`, `unpin`, `react`, `startConversation`, `setTyping`, `listApprovals`, `approveReply`, `rejectReply` | +| `knowledge()` | `list`, `create`, `createText`, `createUrl`, `createFile`, `update`, `delete`, `sync`, `search` | | `ads()` | `list`, `external`, `boostable`, `connections`, `sources`, `authorizeMeta`, `deleteConnection`, `boost`, `create`, `refresh`, `setStatus`, `delete`, `accountTree`, `createCampaign`, `campaign`, `updateCampaign`, `deleteCampaign`, `duplicateCampaign`, `createAdSet`, `adSet`, `updateAdSet`, `deleteAdSet`, `duplicateAdSet`, `createNetworkAd`, `networkAd`, `updateNetworkAd`, `deleteNetworkAd`, `duplicateNetworkAd`, `bulkSetStatus`, `creatives`, `createCreative`, `creative`, `deleteCreative`, `estimateReach`, `insights`, `adInsights`, `audiences`, `createAudience`, `audience`, `updateAudience`, `deleteAudience`, `addAudienceUsers`, `searchTargeting`, `leadForms`, `createLeadForm`, `leadForm`, `archiveLeadForm`, `leads`, `leadsFeed`, `leadPages`, `subscribeLeadPage`, `unsubscribeLeadPage` | | `validate()` | `post`, `length`, `media` | diff --git a/src/main/java/com/fopost/sdk/FoPost.java b/src/main/java/com/fopost/sdk/FoPost.java index 9094803..c640497 100644 --- a/src/main/java/com/fopost/sdk/FoPost.java +++ b/src/main/java/com/fopost/sdk/FoPost.java @@ -13,6 +13,7 @@ import com.fopost.sdk.resource.AnalyticsResource; import com.fopost.sdk.resource.AutomationsResource; import com.fopost.sdk.resource.InboxResource; +import com.fopost.sdk.resource.KnowledgeResource; import com.fopost.sdk.resource.LabelsResource; import com.fopost.sdk.resource.MediaResource; import com.fopost.sdk.resource.PostsResource; @@ -60,6 +61,7 @@ public final class FoPost { private final AccountsResource accounts; private final AccountGroupsResource accountGroups; private final WorkspacesResource workspaces; + private final KnowledgeResource knowledge; private final LabelsResource labels; private final WebhooksResource webhooks; private final AnalyticsResource analytics; @@ -76,6 +78,7 @@ private FoPost(ApiClient http) { this.accounts = new AccountsResource(http); this.accountGroups = new AccountGroupsResource(http); this.workspaces = new WorkspacesResource(http); + this.knowledge = new KnowledgeResource(http); this.labels = new LabelsResource(http); this.webhooks = new WebhooksResource(http); this.analytics = new AnalyticsResource(http); @@ -116,6 +119,11 @@ public WorkspacesResource workspaces() { return workspaces; } + /** The workspace knowledge base, which grounds drafted replies. */ + public KnowledgeResource knowledge() { + return knowledge; + } + public LabelsResource labels() { return labels; } diff --git a/src/main/java/com/fopost/sdk/internal/ApiClient.java b/src/main/java/com/fopost/sdk/internal/ApiClient.java index 2cc76b8..57f16dc 100644 --- a/src/main/java/com/fopost/sdk/internal/ApiClient.java +++ b/src/main/java/com/fopost/sdk/internal/ApiClient.java @@ -76,6 +76,10 @@ public JsonNode put(String path, Object body) { return request("PUT", path, body, null); } + public JsonNode patch(String path, Object body) { + return request("PATCH", path, body, null); + } + public JsonNode delete(String path) { return request("DELETE", path, null, null); } diff --git a/src/main/java/com/fopost/sdk/model/KnowledgeMatch.java b/src/main/java/com/fopost/sdk/model/KnowledgeMatch.java new file mode 100644 index 0000000..7bb8bd3 --- /dev/null +++ b/src/main/java/com/fopost/sdk/model/KnowledgeMatch.java @@ -0,0 +1,14 @@ +package com.fopost.sdk.model; + +/** + * One retrieved passage, with the source it came from so a reply can cite it. + * + * @param score similarity to the question, 0-1 + */ +public record KnowledgeMatch( + String sourceId, + String sourceTitle, + String sourceKind, + String sourceUrl, + String text, + Double score) {} diff --git a/src/main/java/com/fopost/sdk/model/KnowledgeSource.java b/src/main/java/com/fopost/sdk/model/KnowledgeSource.java new file mode 100644 index 0000000..cb72c73 --- /dev/null +++ b/src/main/java/com/fopost/sdk/model/KnowledgeSource.java @@ -0,0 +1,32 @@ +package com.fopost.sdk.model; + +import java.time.Instant; + +/** + * One thing the workspace has told FoPost about itself: an FAQ, a note, a page + * on its own site, or a plain-text/CSV file from the media library. + * + * @param kind {@code faq}, {@code text}, {@code url} or {@code file} + * @param status {@code pending}, {@code syncing}, {@code ready} or {@code failed}; + * only a ready source is searched + * @param statusMessage why the last sync failed, in plain words + * @param url set for {@code url} sources + * @param mediaId set for {@code file} sources: the media library item read + * @param brandVoiceId null means the source serves the whole workspace + * @param chunkCount searchable passages the last sync produced + * @param content the typed text, for {@code faq} and {@code text} sources only + */ +public record KnowledgeSource( + String id, + String kind, + String title, + String status, + String statusMessage, + String url, + String mediaId, + String brandVoiceId, + Integer chunkCount, + String content, + Instant lastSyncedAt, + Instant createdAt, + Instant updatedAt) {} diff --git a/src/main/java/com/fopost/sdk/resource/KnowledgeResource.java b/src/main/java/com/fopost/sdk/resource/KnowledgeResource.java new file mode 100644 index 0000000..98f6b19 --- /dev/null +++ b/src/main/java/com/fopost/sdk/resource/KnowledgeResource.java @@ -0,0 +1,137 @@ +package com.fopost.sdk.resource; + +import com.fopost.sdk.internal.ApiClient; +import com.fopost.sdk.model.KnowledgeMatch; +import com.fopost.sdk.model.KnowledgeSource; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * The workspace knowledge base: what the workspace has told FoPost about + * itself. + * + *
A source is an FAQ, a note, a page on your own site, or a plain-text/CSV
+ * item from the media library. Retrieval over these is what grounds a drafted
+ * inbox reply in your own answers instead of an invented one. Needs the {@code
+ * inbox} scope.
+ */
+public final class KnowledgeResource {
+
+ private final ApiClient http;
+
+ public KnowledgeResource(ApiClient http) {
+ this.http = http;
+ }
+
+ public List {@code kind} is {@code faq}, {@code text}, {@code url} or {@code file}. An {@code faq} or
+ * {@code text} source needs {@code content}, a {@code url} source needs {@code url}, and a
+ * {@code file} source needs {@code mediaId} pointing at a plain-text or CSV item in the same
+ * workspace. Pass null for whatever the kind does not use.
+ */
+ public KnowledgeSource create(
+ String kind,
+ String title,
+ String content,
+ String url,
+ String mediaId,
+ String brandVoiceId,
+ String workspaceId) {
+ Map