Skip to content

Commit 37e5816

Browse files
Feature/conversation backport to 2.x (#1286) (#1297)
* Conversational Memory for GenAI Apps (#1196) * moved code over Signed-off-by: HenryL27 <[email protected]> * added actions to MLPlugin; fixed io lib stuff Signed-off-by: HenryL27 <[email protected]> * fixed copyrights again Signed-off-by: HenryL27 <[email protected]> * Fix nullptr exception in .equals Signed-off-by: HenryL27 <[email protected]> * preserve thread context across action calls Signed-off-by: HenryL27 <[email protected]> * remove MissingResourceException from CreatInteractionRequest in favor of IOException Signed-off-by: HenryL27 <[email protected]> * move ConversationMet, Interaction, and Constants to common/conversational Signed-off-by: HenryL27 <[email protected]> * Sequentialize createInteraction to remove data race Signed-off-by: HenryL27 <[email protected]> * allow disorder when conversations have same timestamp Signed-off-by: HenryL27 <[email protected]> * lombokify Signed-off-by: HenryL27 <[email protected]> * add some unit testing Signed-off-by: HenryL27 <[email protected]> * Increase unit test coverage Signed-off-by: HenryL27 <[email protected]> * fix naming Signed-off-by: HenryL27 <[email protected]> * finish code coverage for actions Signed-off-by: HenryL27 <[email protected]> * Leave null values out of XContent per #1196 (comment) Signed-off-by: HenryL27 <[email protected]> * Add integration tests for rest actions Signed-off-by: HenryL27 <[email protected]> * apply spotless Signed-off-by: HenryL27 <[email protected]> * Complete unit testing for Index classes Signed-off-by: HenryL27 <[email protected]> * update build.gradle Signed-off-by: HenryL27 <[email protected]> * Finish unit tests Signed-off-by: HenryL27 <[email protected]> * Fail closed on missing convo access Signed-off-by: HenryL27 <[email protected]> * address code review/walkthrough comments Signed-off-by: HenryL27 <[email protected]> * re-add prompt temlplate and metadata fields at interaction level Signed-off-by: HenryL27 <[email protected]> * parse request body, not params, for post requests Signed-off-by: HenryL27 <[email protected]> * restructure with memory as higher-level term Signed-off-by: HenryL27 <[email protected]> * clean up build.gradle Signed-off-by: HenryL27 <[email protected]> * apply spotless Signed-off-by: HenryL27 <[email protected]> * change interaction field names timestamp -> create_time metadata -> additional_info Signed-off-by: HenryL27 <[email protected]> * fix GetInteractionsResponse xcontent tests Signed-off-by: HenryL27 <[email protected]> * propagate name change to variables and parameters Signed-off-by: HenryL27 <[email protected]> * clean logging and fix typos Signed-off-by: HenryL27 <[email protected]> * fix final convtructor according to find-and-replace Signed-off-by: HenryL27 <[email protected]> * append plugin-ml- to index names Signed-off-by: HenryL27 <[email protected]> --------- Signed-off-by: HenryL27 <[email protected]> * Feature/conversation memory feature flag (#1271) * add feature flag and checks to transport actions Signed-off-by: HenryL27 <[email protected]> * add feature flag tests Signed-off-by: HenryL27 <[email protected]> * fix typos for real with find-and-replace Signed-off-by: HenryL27 <[email protected]> * rename conversational-memory directory to memory Signed-off-by: HenryL27 <[email protected]> * fix settings.gradle with new dir name Signed-off-by: HenryL27 <[email protected]> * re-add feature flag checks and tests to transport layer Signed-off-by: HenryL27 <[email protected]> * fix feature flag with updateConsumer Signed-off-by: HenryL27 <[email protected]> * remove redundant settings update Signed-off-by: HenryL27 <[email protected]> * clean up feature var initialization to avoid unchecked conversion warning Signed-off-by: HenryL27 <[email protected]> --------- Signed-off-by: HenryL27 <[email protected]> * Use Search Pipeline processors, Remote Inference and HttpConnector to enable Retrieval Augmented Generation (RAG) (#1195) * Use Search Pipeline processors, Remote Inference and HttpConnector to enable Retrieval Augmented Generation (RAG) (#1150) Signed-off-by: Austin Lee <[email protected]> * Address test coverage. Signed-off-by: Austin Lee <[email protected]> * Fix/update imports due to changes coming from core. Signed-off-by: Austin Lee <[email protected]> * Update license header. Signed-off-by: Austin Lee <[email protected]> * Address comments. Signed-off-by: Austin Lee <[email protected]> * Use List for context fields so we can pull contexts from multiple fields when constructing contexts for LLMs. Signed-off-by: Austin Lee <[email protected]> * Address review comments. Signed-off-by: Austin Lee <[email protected]> * Fix spotless issue. Signed-off-by: Austin Lee <[email protected]> * Update README. Signed-off-by: Austin Lee <[email protected]> * Fix ml-client shadowJar implicit dependency issue. Signed-off-by: Austin Lee <[email protected]> * Add a wrapper client for ML predict. Signed-off-by: Austin Lee <[email protected]> * Add tests for the internal ML client. Signed-off-by: Austin Lee <[email protected]> --------- Signed-off-by: Austin Lee <[email protected]> Signed-off-by: Austin Lee <[email protected]> Signed-off-by: HenryL27 <[email protected]> * [Feature] Add Retrieval Augmented Generation search processors (#1275) * Put RAG pipeline behind a feature flag. Signed-off-by: Austin Lee <[email protected]> * Add support for chat history in RAG using the Conversational Memory API Signed-off-by: Austin Lee <[email protected]> * Fix spotless Signed-off-by: Austin Lee <[email protected]> * Fix RAG feature flag enablement. Signed-off-by: Austin Lee <[email protected]> * Address review comments and suggestions. Signed-off-by: Austin Lee <[email protected]> * Address comments. Signed-off-by: Austin Lee <[email protected]> * Add unit tests for MachineLearningPlugin Signed-off-by: Austin Lee <[email protected]> --------- Signed-off-by: Austin Lee <[email protected]> Signed-off-by: HenryL27 <[email protected]> * Allow RAG pipeline feature flag to be enabled and disabled dynamically (#1293) * Allow RAG pipeline feature flag to be enabled and disabled dynamically. Signed-off-by: Austin Lee <[email protected]> * Address review comments. Signed-off-by: Austin Lee <[email protected]> * Add negative test cases for RAG feature flag being turned off. Signed-off-by: Austin Lee <[email protected]> * Improve error checking. Signed-off-by: Austin Lee <[email protected]> --------- Signed-off-by: Austin Lee <[email protected]> Signed-off-by: HenryL27 <[email protected]> * apply spotless Signed-off-by: HenryL27 <[email protected]> --------- Signed-off-by: HenryL27 <[email protected]> Signed-off-by: Austin Lee <[email protected]> Signed-off-by: Austin Lee <[email protected]> Co-authored-by: Austin Lee <[email protected]> (cherry picked from commit 1112612) Co-authored-by: HenryL27 <[email protected]>
1 parent 86eb953 commit 37e5816

File tree

118 files changed

+12514
-6
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+12514
-6
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2023 Aryn
3+
* Copyright OpenSearch Contributors
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.opensearch.ml.common.conversation;
19+
20+
/**
21+
* Constants for conversational actions
22+
*/
23+
public class ActionConstants {
24+
25+
/** name of conversation Id field in all responses */
26+
public final static String CONVERSATION_ID_FIELD = "conversation_id";
27+
28+
/** name of list of conversations in all responses */
29+
public final static String RESPONSE_CONVERSATION_LIST_FIELD = "conversations";
30+
/** name of list on interactions in all responses */
31+
public final static String RESPONSE_INTERACTION_LIST_FIELD = "interactions";
32+
/** name of interaction Id field in all responses */
33+
public final static String RESPONSE_INTERACTION_ID_FIELD = "interaction_id";
34+
35+
/** name of conversation name in all requests */
36+
public final static String REQUEST_CONVERSATION_NAME_FIELD = "name";
37+
/** name of maxResults field name in all requests */
38+
public final static String REQUEST_MAX_RESULTS_FIELD = "max_results";
39+
/** name of nextToken field name in all messages */
40+
public final static String NEXT_TOKEN_FIELD = "next_token";
41+
/** name of input field in all requests */
42+
public final static String INPUT_FIELD = "input";
43+
/** name of AI response field in all respopnses */
44+
public final static String AI_RESPONSE_FIELD = "response";
45+
/** name of origin field in all requests */
46+
public final static String RESPONSE_ORIGIN_FIELD = "origin";
47+
/** name of prompt template field in all requests */
48+
public final static String PROMPT_TEMPLATE_FIELD = "prompt_template";
49+
/** name of metadata field in all requests */
50+
public final static String ADDITIONAL_INFO_FIELD = "additional_info";
51+
/** name of success field in all requests */
52+
public final static String SUCCESS_FIELD = "success";
53+
54+
/** path for create conversation */
55+
public final static String CREATE_CONVERSATION_REST_PATH = "/_plugins/_ml/memory/conversation";
56+
/** path for list conversations */
57+
public final static String GET_CONVERSATIONS_REST_PATH = "/_plugins/_ml/memory/conversation";
58+
/** path for put interaction */
59+
public final static String CREATE_INTERACTION_REST_PATH = "/_plugins/_ml/memory/conversation/{conversation_id}";
60+
/** path for get interactions */
61+
public final static String GET_INTERACTIONS_REST_PATH = "/_plugins/_ml/memory/conversation/{conversation_id}";
62+
/** path for delete conversation */
63+
public final static String DELETE_CONVERSATION_REST_PATH = "/_plugins/_ml/memory/conversation/{conversation_id}";
64+
65+
/** default max results returned by get operations */
66+
public final static int DEFAULT_MAX_RESULTS = 10;
67+
68+
/** default username for reporting security errors if no or malformed username */
69+
public final static String DEFAULT_USERNAME_FOR_ERRORS = "BAD_USER";
70+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright 2023 Aryn
3+
* Copyright OpenSearch Contributors
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.opensearch.ml.common.conversation;
19+
20+
import java.io.IOException;
21+
import java.time.Instant;
22+
import java.util.Map;
23+
import java.util.Objects;
24+
25+
import org.opensearch.action.index.IndexRequest;
26+
import org.opensearch.core.common.io.stream.StreamInput;
27+
import org.opensearch.core.common.io.stream.StreamOutput;
28+
import org.opensearch.core.common.io.stream.Writeable;
29+
import org.opensearch.core.xcontent.ToXContentObject;
30+
import org.opensearch.core.xcontent.XContentBuilder;
31+
import org.opensearch.search.SearchHit;
32+
33+
import lombok.AllArgsConstructor;
34+
import lombok.Getter;
35+
36+
/**
37+
* Class for holding conversational metadata
38+
*/
39+
@AllArgsConstructor
40+
public class ConversationMeta implements Writeable, ToXContentObject {
41+
42+
@Getter
43+
private String id;
44+
@Getter
45+
private Instant createdTime;
46+
@Getter
47+
private String name;
48+
@Getter
49+
private String user;
50+
51+
/**
52+
* Creates a conversationMeta object from a SearchHit object
53+
* @param hit the search hit to transform into a conversationMeta object
54+
* @return a new conversationMeta object representing the search hit
55+
*/
56+
public static ConversationMeta fromSearchHit(SearchHit hit) {
57+
String id = hit.getId();
58+
return ConversationMeta.fromMap(id, hit.getSourceAsMap());
59+
}
60+
61+
/**
62+
* Creates a conversationMeta object from a Map of fields in the OS index
63+
* @param id the conversation's id
64+
* @param docFields the map of source fields
65+
* @return a new conversationMeta object representing the map
66+
*/
67+
public static ConversationMeta fromMap(String id, Map<String, Object> docFields) {
68+
Instant created = Instant.parse((String) docFields.get(ConversationalIndexConstants.META_CREATED_FIELD));
69+
String name = (String) docFields.get(ConversationalIndexConstants.META_NAME_FIELD);
70+
String user = (String) docFields.get(ConversationalIndexConstants.USER_FIELD);
71+
return new ConversationMeta(id, created, name, user);
72+
}
73+
74+
/**
75+
* Creates a conversationMeta from a stream, given the stream was written to by
76+
* conversationMeta.writeTo
77+
* @param in stream to read from
78+
* @return new conversationMeta object
79+
* @throws IOException if you're reading from a stream without a conversationMeta in it
80+
*/
81+
public static ConversationMeta fromStream(StreamInput in) throws IOException {
82+
String id = in.readString();
83+
Instant created = in.readInstant();
84+
String name = in.readString();
85+
String user = in.readOptionalString();
86+
return new ConversationMeta(id, created, name, user);
87+
}
88+
89+
@Override
90+
public void writeTo(StreamOutput out) throws IOException {
91+
out.writeString(id);
92+
out.writeInstant(createdTime);
93+
out.writeString(name);
94+
out.writeOptionalString(user);
95+
}
96+
97+
98+
/**
99+
* Convert this conversationMeta object into an IndexRequest so it can be indexed
100+
* @param index the index to send this conversation to. Should usually be .conversational-meta
101+
* @return the IndexRequest for the client to send
102+
*/
103+
public IndexRequest toIndexRequest(String index) {
104+
IndexRequest request = new IndexRequest(index);
105+
return request.id(this.id).source(
106+
ConversationalIndexConstants.META_CREATED_FIELD, this.createdTime,
107+
ConversationalIndexConstants.META_NAME_FIELD, this.name
108+
);
109+
}
110+
111+
@Override
112+
public String toString() {
113+
return "{id=" + id
114+
+ ", name=" + name
115+
+ ", created=" + createdTime.toString()
116+
+ ", user=" + user
117+
+ "}";
118+
}
119+
120+
@Override
121+
public XContentBuilder toXContent(XContentBuilder builder, ToXContentObject.Params params) throws IOException {
122+
builder.startObject();
123+
builder.field(ActionConstants.CONVERSATION_ID_FIELD, this.id);
124+
builder.field(ConversationalIndexConstants.META_CREATED_FIELD, this.createdTime);
125+
builder.field(ConversationalIndexConstants.META_NAME_FIELD, this.name);
126+
if(this.user != null) {
127+
builder.field(ConversationalIndexConstants.USER_FIELD, this.user);
128+
}
129+
builder.endObject();
130+
return builder;
131+
}
132+
133+
@Override
134+
public boolean equals(Object other) {
135+
if(!(other instanceof ConversationMeta)) {
136+
return false;
137+
}
138+
ConversationMeta otherConversation = (ConversationMeta) other;
139+
return Objects.equals(this.id, otherConversation.id) &&
140+
Objects.equals(this.user, otherConversation.user) &&
141+
Objects.equals(this.createdTime, otherConversation.createdTime) &&
142+
Objects.equals(this.name, otherConversation.name);
143+
}
144+
145+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright 2023 Aryn
3+
* Copyright OpenSearch Contributors
4+
* SPDX-License-Identifier: Apache-2.0
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.opensearch.ml.common.conversation;
19+
20+
import org.opensearch.common.settings.Setting;
21+
22+
/**
23+
* Class containing a bunch of constant defining how the conversational indices are formatted
24+
*/
25+
public class ConversationalIndexConstants {
26+
/** Version of the meta index schema */
27+
public final static Integer META_INDEX_SCHEMA_VERSION = 1;
28+
/** Name of the conversational metadata index */
29+
public final static String META_INDEX_NAME = ".plugins-ml-conversation-meta";
30+
/** Name of the metadata field for initial timestamp */
31+
public final static String META_CREATED_FIELD = "create_time";
32+
/** Name of the metadata field for name of the conversation */
33+
public final static String META_NAME_FIELD = "name";
34+
/** Name of the owning user field in all indices */
35+
public final static String USER_FIELD = "user";
36+
/** Mappings for the conversational metadata index */
37+
public final static String META_MAPPING = "{\n"
38+
+ " \"_meta\": {\n"
39+
+ " \"schema_version\": " + META_INDEX_SCHEMA_VERSION + "\n"
40+
+ " },\n"
41+
+ " \"properties\": {\n"
42+
+ " \""
43+
+ META_NAME_FIELD
44+
+ "\": {\"type\": \"keyword\"},\n"
45+
+ " \""
46+
+ META_CREATED_FIELD
47+
+ "\": {\"type\": \"date\", \"format\": \"strict_date_time||epoch_millis\"},\n"
48+
+ " \""
49+
+ USER_FIELD
50+
+ "\": {\"type\": \"keyword\"}\n"
51+
+ " }\n"
52+
+ "}";
53+
54+
/** Version of the interactions index schema */
55+
public final static Integer INTERACTIONS_INDEX_SCHEMA_VERSION = 1;
56+
/** Name of the conversational interactions index */
57+
public final static String INTERACTIONS_INDEX_NAME = ".plugins-ml-conversation-interactions";
58+
/** Name of the interaction field for the conversation Id */
59+
public final static String INTERACTIONS_CONVERSATION_ID_FIELD = "conversation_id";
60+
/** Name of the interaction field for the human input */
61+
public final static String INTERACTIONS_INPUT_FIELD = "input";
62+
/** Name of the interaction field for the prompt template */
63+
public final static String INTERACTIONS_PROMPT_TEMPLATE_FIELD = "prompt_template";
64+
/** Name of the interaction field for the AI response */
65+
public final static String INTERACTIONS_RESPONSE_FIELD = "response";
66+
/** Name of the interaction field for the response's origin */
67+
public final static String INTERACTIONS_ORIGIN_FIELD = "origin";
68+
/** Name of the interaction field for additional metadata */
69+
public final static String INTERACTIONS_ADDITIONAL_INFO_FIELD = "additional_info";
70+
/** Name of the interaction field for the timestamp */
71+
public final static String INTERACTIONS_CREATE_TIME_FIELD = "create_time";
72+
/** Mappings for the interactions index */
73+
public final static String INTERACTIONS_MAPPINGS = "{\n"
74+
+ " \"_meta\": {\n"
75+
+ " \"schema_version\": " + INTERACTIONS_INDEX_SCHEMA_VERSION + "\n"
76+
+ " },\n"
77+
+ " \"properties\": {\n"
78+
+ " \""
79+
+ INTERACTIONS_CONVERSATION_ID_FIELD
80+
+ "\": {\"type\": \"keyword\"},\n"
81+
+ " \""
82+
+ INTERACTIONS_CREATE_TIME_FIELD
83+
+ "\": {\"type\": \"date\", \"format\": \"strict_date_time||epoch_millis\"},\n"
84+
+ " \""
85+
+ INTERACTIONS_INPUT_FIELD
86+
+ "\": {\"type\": \"text\"},\n"
87+
+ " \""
88+
+ INTERACTIONS_PROMPT_TEMPLATE_FIELD
89+
+ "\": {\"type\": \"text\"},\n"
90+
+ " \""
91+
+ INTERACTIONS_RESPONSE_FIELD
92+
+ "\": {\"type\": \"text\"},\n"
93+
+ " \""
94+
+ INTERACTIONS_ORIGIN_FIELD
95+
+ "\": {\"type\": \"keyword\"},\n"
96+
+ " \""
97+
+ INTERACTIONS_ADDITIONAL_INFO_FIELD
98+
+ "\": {\"type\": \"text\"}\n"
99+
+ " }\n"
100+
+ "}";
101+
102+
/** Feature Flag setting for conversational memory */
103+
public static final Setting<Boolean> ML_COMMONS_MEMORY_FEATURE_ENABLED = Setting
104+
.boolSetting("plugins.ml_commons.memory_feature_enabled", false, Setting.Property.NodeScope, Setting.Property.Dynamic);
105+
}

0 commit comments

Comments
 (0)