Skip to content

Prompt Management Create API #3779

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: feature/prompt
Choose a base branch
from

Conversation

rcho93
Copy link

@rcho93 rcho93 commented Apr 26, 2025

Description

Implements an API to create prompt, and creates a new system index for prompt

Related Issues

Resolves #[Issue number to be closed when this PR is merged]

Check List

  • New functionality includes testing.
  • New functionality has been documented.
  • API changes companion pull request created.
  • Commits are signed per the DCO using --signoff.
  • Public documentation issue/PR created.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@dhrubo-os
Copy link
Collaborator

Apply spotless: ./gradlew spotlessApply

import java.io.IOException;
import java.io.UncheckedIOException;

import org.opensearch.ml.common.utils.IndexUtils;

import static org.opensearch.ml.common.CommonValue.*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use * in import. Disable wilecard import: https://www.baeldung.com/intellij-disable-wildcard-import

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Thank you!

if (tag != null) {
builder.field(TAG_FIELD, tag);
}
if (createTime != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tenantid is missing here.

.build();
}

/*public void update(MLCreatePromptInput updateContent) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not commit my changes frequently while working on this project. To avoid ending up with a large PR that would be difficult for reviewers to go through, I am creating multiple smaller PRs instead.
So, I am manually commenting out unrelated code so that each PR stays focused on a specific topic. I will be using this method for Update api.

pyek-bot and others added 3 commits April 25, 2025 21:54
…mpanied by text (opensearch-project#3755)

* fix: handle model response when toolUse is not accompanied by text

Signed-off-by: Pavan Yekbote <[email protected]>

* feat: add test case for parseLLMOutput

Signed-off-by: Pavan Yekbote <[email protected]>

---------

Signed-off-by: Pavan Yekbote <[email protected]>
…ent empty response (opensearch-project#3756)

* fix: expose max_iteration for react

Signed-off-by: Pavan Yekbote <[email protected]>

* fix: defaults for agent execution and differentiate between step and step result

Signed-off-by: Pavan Yekbote <[email protected]>

* fix: return react agent id in agent response to expose more details

Signed-off-by: Pavan Yekbote <[email protected]>

* spotless

Signed-off-by: Pavan Yekbote <[email protected]>

* fix: remove test prompt from react system prompt

Signed-off-by: Pavan Yekbote <[email protected]>

* refactor: rename parameters exposed to user to executor

Signed-off-by: Pavan Yekbote <[email protected]>

* fix: give user complete control over planner system prompt

Signed-off-by: Pavan Yekbote <[email protected]>

---------

Signed-off-by: Pavan Yekbote <[email protected]>
@rcho93
Copy link
Author

rcho93 commented Apr 26, 2025

@ohltyler, Hey Tyler, This is my first draft PR for CREATE API for pormpt management:)


@Getter
@EqualsAndHashCode
public class MLPrompt implements ToXContentObject, Writeable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add java doc for every class and main method so that it's readable

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a new file, a header is needed. Check the other classes and copy the header in all new files.
/*

  • Copyright OpenSearch Contributors
  • SPDX-License-Identifier: Apache-2.0
    */

}

public MLPrompt(StreamInput input) throws IOException {
Version streamInputVersion = input.getVersion();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is the version a mandatory field?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think the version should be a mandatory field, and should throw an error if the version field is not provided by the user.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we do version has a default one if no version is required? I would assume when I don't put a version, it has an initial version.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I will make the version field optional. Thank you!

this.prompt = input.readMap(s -> s.readString(), s -> s.readString());
}
this.tag = input.readOptionalString();
tenantId = streamInputVersion.onOrAfter(VERSION_2_19_0) ? input.readOptionalString() : null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.tenantId?


private void indexPrompt(MLPrompt prompt, ActionListener<MLCreatePromptResponse> listener) {
log.info("prompt created, indexing into the prompt system index");
mlIndicesHandler.initMLPromptIndex(ActionListener.wrap(indexCreated -> {
Copy link
Collaborator

@mingshl mingshl Apr 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when creating the index, do you have validation to check if the prompt id is existed? can you create a prompt twice with the same prompt id?

also thinking can user create two prompt with the same name? have you consider the scenarios?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to make prompt create api consistent as the other create apis, like connector. I noticed that the connector_id is set to a document id that is generated when a new document is stored in index. So, I followed the same mechanism without including any validation checks on promptId, since we are not manually creating one. Naming as well, other ml-commons features such as connector and model don't have a unique name and do not throw any errors when existing name is used.

"type": "text"
},
"prompt": {
"type": "flat_object"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you trying add tags later?

Copy link
Contributor

@brianf-aws brianf-aws left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exciting work so far lets keep it up! 😄

@@ -119,6 +123,7 @@ public void initMLIndexIfAbsent(MLIndex index, ActionListener<Boolean> listener)
log.debug("index:{} is already created", indexName);
if (indexMappingUpdated.containsKey(indexName) && !indexMappingUpdated.get(indexName).get()) {
shouldUpdateIndex(indexName, index.getVersion(), ActionListener.wrap(r -> {
log.info("r is " + r);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now looking back is this a log for your dev purpose? If so lets delete it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^

out.writeOptionalString(name);
out.writeOptionalString(description);
if (prompt != null) {
out.writeBoolean(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not to clear what field this boolean pertains to I'm assuming its a update version sort of boolean can we refactor so we assign some variable affected by this to make it clear to the reader what is this related to?

String description,
Map<String, String> prompt,
String tag,
boolean dryRun,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats your interpretation of dryRun?

Version streamInputVersion = input.getVersion();
name = input.readOptionalString();
description = input.readOptionalString();
if (input.readBoolean()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updatePrompt = input.readBoolean();

if(updatePrompt){
// Read from streamInput logic
}

tag = input.readOptionalString();
dryRun = input.readBoolean();
if (streamInputVersion.onOrAfter(VERSION_2_19_0)) {
input.readOptionalString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we read here and not use and not store does this advance to the next item?

public ActionRequestValidationException validate() {
ActionRequestValidationException exception = null;
if (mlCreatePromptInput == null) {
exception = addValidationError("ML Prompt input can't be null", exception);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • If this string gets used over "ML Prompt input can't be null lets put it in a file to reference later if you intend to reuse the string over other actions/methods.


private static final Version MINIMAL_SUPPORTED_VERSION_FOR_CLIENT_CONFIG = CommonValue.VERSION_2_13_0;

public static final String DRY_RUN_PROMPT_NAME = "dryRunPrompt";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Walk me though this variable what are its implications I see this variable used a lot "dryRyn*"

try (ThreadContext.StoredContext context = client.threadPool().getThreadContext().stashContext()) {
prompt.setCreateTime(Instant.now());
prompt.setLastUpdateTime(Instant.now());
sdkClient
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets do some refactoring to avoid callback hell

http://callbackhell.com/


@Getter
@EqualsAndHashCode
public class MLPrompt implements ToXContentObject, Writeable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a new file, a header is needed. Check the other classes and copy the header in all new files.
/*

  • Copyright OpenSearch Contributors
  • SPDX-License-Identifier: Apache-2.0
    */

Comment on lines 65 to 79
this.name = name;
this.description = description;
this.prompt = prompt;
this.tag = tag;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any of these shouldn't be null? I think at least the name shouldn't be null, so it's better to add validation here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I think name and prompt should not be null and force user to specify fields additionally do we think we ought to make prompt not be empty as well (@Rycho)?

Comment on lines +66 to +96
if (!request.hasContent()) {
throw new IOException("Create Prompt request has empty body");
}
XContentParser parser = request.contentParser();
ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser);
MLCreatePromptInput mlCreatePromptInput = MLCreatePromptInput.parse(parser);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to either add validation in here or in the MLCreatePromptInput

pyek-bot and others added 7 commits April 28, 2025 16:22
* initial commit for MCP server in OpenSearch

Signed-off-by: zane-neo <[email protected]>

* Make change to support register or remove tools across cluster

Signed-off-by: zane-neo <[email protected]>

* format code

Signed-off-by: zane-neo <[email protected]>

* fix UT failure caused by code change

Signed-off-by: zane-neo <[email protected]>

* format code

Signed-off-by: zane-neo <[email protected]>

* format code

Signed-off-by: zane-neo <[email protected]>

* add license header

Signed-off-by: zane-neo <[email protected]>

* fix notifications initialized not respond issue

Signed-off-by: zane-neo <[email protected]>

* fix minor issues and add UTs

Signed-off-by: zane-neo <[email protected]>

* Add more UTs

Signed-off-by: zane-neo <[email protected]>

---------

Signed-off-by: zane-neo <[email protected]>
…3795)

(cherry picked from commit 3f503f1)

Signed-off-by: Peter Zhu <[email protected]>
Co-authored-by: Peter Zhu <[email protected]>
* Increment version to 3.1.0-SNAPSHOT

Signed-off-by: opensearch-ci-bot <[email protected]>

* Update build.gradle

Signed-off-by: Peter Zhu <[email protected]>

---------

Signed-off-by: opensearch-ci-bot <[email protected]>
Signed-off-by: Peter Zhu <[email protected]>
Co-authored-by: opensearch-ci-bot <[email protected]>
Co-authored-by: Peter Zhu <[email protected]>
* support MCP session management

Signed-off-by: zane-neo <[email protected]>

* Addressing comments

Signed-off-by: zane-neo <[email protected]>

* add feature flag for mcp server and renaming mcp connector feature flag

Signed-off-by: zane-neo <[email protected]>

* Address critical comments in opensearch-project#3781

Signed-off-by: zane-neo <[email protected]>

---------

Signed-off-by: zane-neo <[email protected]>
* upgrade http client to versoin align with core

Signed-off-by: zane-neo <[email protected]>

* upgrade httpclient-h2 to correct versiono

Signed-off-by: zane-neo <[email protected]>

* use placeholder approach

Signed-off-by: zane-neo <[email protected]>

---------

Signed-off-by: zane-neo <[email protected]>
Copy link
Contributor

@brianf-aws brianf-aws left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there; I think we should tweak the exception messages but other than that looks good to me

}
if (!prompt.containsKey(PROMPT_FIELD_SYSTEM_PROMPT)) {
throw new IllegalArgumentException("Prompt prompt field require system prompt");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar flow for ones in same method

Suggested change
throw new IllegalArgumentException("Prompt prompt field require system prompt");
throw new IllegalArgumentException("MLPrompt prompt field requires {PROMPT_FIELD_SYSTEM_PROMPT} parameter");

@@ -166,7 +179,7 @@ public static MLCreatePromptInput parse(XContentParser parser, boolean updatePro
break;
}
}
return new MLCreatePromptInput(name, description, prompt, tag, tenantId, updatePrompt);
return new MLCreatePromptInput(name, description, version, prompt, tags, tenantId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh can we not use the builder here?

private void handlePromptPutFailure(Exception cause, ActionListener<MLCreatePromptResponse> listener) {
log.error("Failed to save ML Prompt", cause);
private void handlePromptPutFailure(Exception cause, ActionListener<MLCreatePromptResponse> listener, String likelyCause) {
log.error("Failed to save ML Prompt: {}", likelyCause, cause);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!!! This makes the method more user friendly do you think we can resuse the method elswhere?

@rcho93 rcho93 requested a deployment to ml-commons-cicd-env-require-approval May 6, 2025 06:05 — with GitHub Actions Waiting
@rcho93 rcho93 requested a deployment to ml-commons-cicd-env-require-approval May 6, 2025 06:05 — with GitHub Actions Waiting
@rcho93 rcho93 requested a deployment to ml-commons-cicd-env-require-approval May 6, 2025 06:05 — with GitHub Actions Waiting
@rcho93 rcho93 requested a deployment to ml-commons-cicd-env-require-approval May 6, 2025 06:05 — with GitHub Actions Waiting
public static final String PROMPT_FIELD_USER_PROMPT = "user";
public static final String PROMPT_FIELD_SYSTEM_PROMPT = "system";

private static final Version MINIMAL_SUPPORTED_VERSION_FOR_CLIENT_CONFIG = CommonValue.VERSION_2_13_0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, just fixed this :)

this.version = input.readOptionalString();
this.prompt = input.readMap(s -> s.readString(), s -> s.readString());
this.tags = input.readOptionalStringList();
this.tenantId = streamInputVersion.onOrAfter(VERSION_2_19_0) ? input.readOptionalString() : null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually do this type of check for the backward compatibility for the older versions. So I don't think we need this type of check here, as multi-tenancy feature is already published.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it!

@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 18:40 — with GitHub Actions Error
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 18:40 — with GitHub Actions Failure
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 18:40 — with GitHub Actions Error
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 18:40 — with GitHub Actions Failure
@rcho93 rcho93 requested a review from dhrubo-os May 6, 2025 18:42
@brianf-aws
Copy link
Contributor

Hey Ryan Lets add UTs for this PR :)

rcho93 added 11 commits May 6, 2025 15:33
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
Signed-off-by: seungwon cho <[email protected]>
@rcho93 rcho93 force-pushed the feature/prompt-management branch from 2b5c24a to e97d7bb Compare May 6, 2025 22:52
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 22:53 — with GitHub Actions Failure
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 22:53 — with GitHub Actions Error
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 22:53 — with GitHub Actions Error
@rcho93 rcho93 had a problem deploying to ml-commons-cicd-env-require-approval May 6, 2025 22:53 — with GitHub Actions Failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants