Skip to content

Commit

Permalink
Feature/conversation backport to 2.x (#1286)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
HenryL27 and austintlee authored Sep 7, 2023
1 parent 86eb953 commit 1112612
Show file tree
Hide file tree
Showing 118 changed files with 12,514 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2023 Aryn
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opensearch.ml.common.conversation;

/**
* Constants for conversational actions
*/
public class ActionConstants {

/** name of conversation Id field in all responses */
public final static String CONVERSATION_ID_FIELD = "conversation_id";

/** name of list of conversations in all responses */
public final static String RESPONSE_CONVERSATION_LIST_FIELD = "conversations";
/** name of list on interactions in all responses */
public final static String RESPONSE_INTERACTION_LIST_FIELD = "interactions";
/** name of interaction Id field in all responses */
public final static String RESPONSE_INTERACTION_ID_FIELD = "interaction_id";

/** name of conversation name in all requests */
public final static String REQUEST_CONVERSATION_NAME_FIELD = "name";
/** name of maxResults field name in all requests */
public final static String REQUEST_MAX_RESULTS_FIELD = "max_results";
/** name of nextToken field name in all messages */
public final static String NEXT_TOKEN_FIELD = "next_token";
/** name of input field in all requests */
public final static String INPUT_FIELD = "input";
/** name of AI response field in all respopnses */
public final static String AI_RESPONSE_FIELD = "response";
/** name of origin field in all requests */
public final static String RESPONSE_ORIGIN_FIELD = "origin";
/** name of prompt template field in all requests */
public final static String PROMPT_TEMPLATE_FIELD = "prompt_template";
/** name of metadata field in all requests */
public final static String ADDITIONAL_INFO_FIELD = "additional_info";
/** name of success field in all requests */
public final static String SUCCESS_FIELD = "success";

/** path for create conversation */
public final static String CREATE_CONVERSATION_REST_PATH = "/_plugins/_ml/memory/conversation";
/** path for list conversations */
public final static String GET_CONVERSATIONS_REST_PATH = "/_plugins/_ml/memory/conversation";
/** path for put interaction */
public final static String CREATE_INTERACTION_REST_PATH = "/_plugins/_ml/memory/conversation/{conversation_id}";
/** path for get interactions */
public final static String GET_INTERACTIONS_REST_PATH = "/_plugins/_ml/memory/conversation/{conversation_id}";
/** path for delete conversation */
public final static String DELETE_CONVERSATION_REST_PATH = "/_plugins/_ml/memory/conversation/{conversation_id}";

/** default max results returned by get operations */
public final static int DEFAULT_MAX_RESULTS = 10;

/** default username for reporting security errors if no or malformed username */
public final static String DEFAULT_USERNAME_FOR_ERRORS = "BAD_USER";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright 2023 Aryn
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opensearch.ml.common.conversation;

import java.io.IOException;
import java.time.Instant;
import java.util.Map;
import java.util.Objects;

import org.opensearch.action.index.IndexRequest;
import org.opensearch.core.common.io.stream.StreamInput;
import org.opensearch.core.common.io.stream.StreamOutput;
import org.opensearch.core.common.io.stream.Writeable;
import org.opensearch.core.xcontent.ToXContentObject;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.search.SearchHit;

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
* Class for holding conversational metadata
*/
@AllArgsConstructor
public class ConversationMeta implements Writeable, ToXContentObject {

@Getter
private String id;
@Getter
private Instant createdTime;
@Getter
private String name;
@Getter
private String user;

/**
* Creates a conversationMeta object from a SearchHit object
* @param hit the search hit to transform into a conversationMeta object
* @return a new conversationMeta object representing the search hit
*/
public static ConversationMeta fromSearchHit(SearchHit hit) {
String id = hit.getId();
return ConversationMeta.fromMap(id, hit.getSourceAsMap());
}

/**
* Creates a conversationMeta object from a Map of fields in the OS index
* @param id the conversation's id
* @param docFields the map of source fields
* @return a new conversationMeta object representing the map
*/
public static ConversationMeta fromMap(String id, Map<String, Object> docFields) {
Instant created = Instant.parse((String) docFields.get(ConversationalIndexConstants.META_CREATED_FIELD));
String name = (String) docFields.get(ConversationalIndexConstants.META_NAME_FIELD);
String user = (String) docFields.get(ConversationalIndexConstants.USER_FIELD);
return new ConversationMeta(id, created, name, user);
}

/**
* Creates a conversationMeta from a stream, given the stream was written to by
* conversationMeta.writeTo
* @param in stream to read from
* @return new conversationMeta object
* @throws IOException if you're reading from a stream without a conversationMeta in it
*/
public static ConversationMeta fromStream(StreamInput in) throws IOException {
String id = in.readString();
Instant created = in.readInstant();
String name = in.readString();
String user = in.readOptionalString();
return new ConversationMeta(id, created, name, user);
}

@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeString(id);
out.writeInstant(createdTime);
out.writeString(name);
out.writeOptionalString(user);
}


/**
* Convert this conversationMeta object into an IndexRequest so it can be indexed
* @param index the index to send this conversation to. Should usually be .conversational-meta
* @return the IndexRequest for the client to send
*/
public IndexRequest toIndexRequest(String index) {
IndexRequest request = new IndexRequest(index);
return request.id(this.id).source(
ConversationalIndexConstants.META_CREATED_FIELD, this.createdTime,
ConversationalIndexConstants.META_NAME_FIELD, this.name
);
}

@Override
public String toString() {
return "{id=" + id
+ ", name=" + name
+ ", created=" + createdTime.toString()
+ ", user=" + user
+ "}";
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, ToXContentObject.Params params) throws IOException {
builder.startObject();
builder.field(ActionConstants.CONVERSATION_ID_FIELD, this.id);
builder.field(ConversationalIndexConstants.META_CREATED_FIELD, this.createdTime);
builder.field(ConversationalIndexConstants.META_NAME_FIELD, this.name);
if(this.user != null) {
builder.field(ConversationalIndexConstants.USER_FIELD, this.user);
}
builder.endObject();
return builder;
}

@Override
public boolean equals(Object other) {
if(!(other instanceof ConversationMeta)) {
return false;
}
ConversationMeta otherConversation = (ConversationMeta) other;
return Objects.equals(this.id, otherConversation.id) &&
Objects.equals(this.user, otherConversation.user) &&
Objects.equals(this.createdTime, otherConversation.createdTime) &&
Objects.equals(this.name, otherConversation.name);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright 2023 Aryn
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opensearch.ml.common.conversation;

import org.opensearch.common.settings.Setting;

/**
* Class containing a bunch of constant defining how the conversational indices are formatted
*/
public class ConversationalIndexConstants {
/** Version of the meta index schema */
public final static Integer META_INDEX_SCHEMA_VERSION = 1;
/** Name of the conversational metadata index */
public final static String META_INDEX_NAME = ".plugins-ml-conversation-meta";
/** Name of the metadata field for initial timestamp */
public final static String META_CREATED_FIELD = "create_time";
/** Name of the metadata field for name of the conversation */
public final static String META_NAME_FIELD = "name";
/** Name of the owning user field in all indices */
public final static String USER_FIELD = "user";
/** Mappings for the conversational metadata index */
public final static String META_MAPPING = "{\n"
+ " \"_meta\": {\n"
+ " \"schema_version\": " + META_INDEX_SCHEMA_VERSION + "\n"
+ " },\n"
+ " \"properties\": {\n"
+ " \""
+ META_NAME_FIELD
+ "\": {\"type\": \"keyword\"},\n"
+ " \""
+ META_CREATED_FIELD
+ "\": {\"type\": \"date\", \"format\": \"strict_date_time||epoch_millis\"},\n"
+ " \""
+ USER_FIELD
+ "\": {\"type\": \"keyword\"}\n"
+ " }\n"
+ "}";

/** Version of the interactions index schema */
public final static Integer INTERACTIONS_INDEX_SCHEMA_VERSION = 1;
/** Name of the conversational interactions index */
public final static String INTERACTIONS_INDEX_NAME = ".plugins-ml-conversation-interactions";
/** Name of the interaction field for the conversation Id */
public final static String INTERACTIONS_CONVERSATION_ID_FIELD = "conversation_id";
/** Name of the interaction field for the human input */
public final static String INTERACTIONS_INPUT_FIELD = "input";
/** Name of the interaction field for the prompt template */
public final static String INTERACTIONS_PROMPT_TEMPLATE_FIELD = "prompt_template";
/** Name of the interaction field for the AI response */
public final static String INTERACTIONS_RESPONSE_FIELD = "response";
/** Name of the interaction field for the response's origin */
public final static String INTERACTIONS_ORIGIN_FIELD = "origin";
/** Name of the interaction field for additional metadata */
public final static String INTERACTIONS_ADDITIONAL_INFO_FIELD = "additional_info";
/** Name of the interaction field for the timestamp */
public final static String INTERACTIONS_CREATE_TIME_FIELD = "create_time";
/** Mappings for the interactions index */
public final static String INTERACTIONS_MAPPINGS = "{\n"
+ " \"_meta\": {\n"
+ " \"schema_version\": " + INTERACTIONS_INDEX_SCHEMA_VERSION + "\n"
+ " },\n"
+ " \"properties\": {\n"
+ " \""
+ INTERACTIONS_CONVERSATION_ID_FIELD
+ "\": {\"type\": \"keyword\"},\n"
+ " \""
+ INTERACTIONS_CREATE_TIME_FIELD
+ "\": {\"type\": \"date\", \"format\": \"strict_date_time||epoch_millis\"},\n"
+ " \""
+ INTERACTIONS_INPUT_FIELD
+ "\": {\"type\": \"text\"},\n"
+ " \""
+ INTERACTIONS_PROMPT_TEMPLATE_FIELD
+ "\": {\"type\": \"text\"},\n"
+ " \""
+ INTERACTIONS_RESPONSE_FIELD
+ "\": {\"type\": \"text\"},\n"
+ " \""
+ INTERACTIONS_ORIGIN_FIELD
+ "\": {\"type\": \"keyword\"},\n"
+ " \""
+ INTERACTIONS_ADDITIONAL_INFO_FIELD
+ "\": {\"type\": \"text\"}\n"
+ " }\n"
+ "}";

/** Feature Flag setting for conversational memory */
public static final Setting<Boolean> ML_COMMONS_MEMORY_FEATURE_ENABLED = Setting
.boolSetting("plugins.ml_commons.memory_feature_enabled", false, Setting.Property.NodeScope, Setting.Property.Dynamic);
}
Loading

0 comments on commit 1112612

Please sign in to comment.