Skip to content

Commit

Permalink
Merge pull request #26 from stoerr/feature/pseudomodels
Browse files Browse the repository at this point in the history
  • Loading branch information
stoerr authored May 14, 2024
2 parents bd10da3 + 65e0b97 commit 3c9622f
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import javax.annotation.Nonnull;

import net.stoerr.ai.aigenpipeline.framework.chat.AIChatBuilder;
import net.stoerr.ai.aigenpipeline.framework.chat.CopyPseudoAIChatBuilderImpl;
import net.stoerr.ai.aigenpipeline.framework.chat.OpenAIChatBuilderImpl;
import net.stoerr.ai.aigenpipeline.framework.task.AIGenerationTask;
import net.stoerr.ai.aigenpipeline.framework.task.AIInOut;
Expand Down Expand Up @@ -125,7 +126,10 @@ protected void run(String[] args) throws IOException {
}

public AIChatBuilder makeChatBuilder() {
AIChatBuilder chatBuilder = new OpenAIChatBuilderImpl();
AIChatBuilder chatBuilder =
CopyPseudoAIChatBuilderImpl.MODEL_COPY.equals(model) ?
new CopyPseudoAIChatBuilderImpl() :
new OpenAIChatBuilderImpl();
if (null != url) {
chatBuilder.url(url);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package net.stoerr.ai.aigenpipeline.framework.chat;

/**
* A pseudo AI chat model that just copies the input to the output.
*/
public class CopyPseudoAIChatBuilderImpl implements AIChatBuilder {

/**
* Pseudo model that just concatenates the inputs and writes these to the output.
*/
public static final String MODEL_COPY = "copy";

protected final StringBuilder allInputs = new StringBuilder();

@Override
public AIChatBuilder url(String url) {
// not used
return this;
}

@Override
public AIChatBuilder key(String key) {
// not used
return this;
}

@Override
public AIChatBuilder organizationId(String organizationId) {
// not used
return this;
}

@Override
public AIChatBuilder maxTokens(int maxTokens) {
// not used
return this;
}

@Override
public AIChatBuilder model(String model) {
if (!MODEL_COPY.equals(model)) {
throw new IllegalArgumentException("Only model " + MODEL_COPY + " is supported.");
}
return this;
}

@Override
public AIChatBuilder systemMsg(String text) {
// not used
return this;
}

@Override
public AIChatBuilder userMsg(String text) {
// not used
return this;
}

/**
* We assume the "put it into the mouth of the AI" pattern is used. Then all the assistant messages are actually
* the inputs. (That is a bit dangerous if it's used differently, but the easiest way.)
*/
@Override
public AIChatBuilder assistantMsg(String text) {
if (text != null) {
allInputs.append(text);
}
return this;
}

@Override
public String toJson() {
return allInputs.toString();
}

@Override
public String execute() {
return allInputs.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
*/
public class OpenAIChatBuilderImpl implements AIChatBuilder {

/**
* Pseudo model that outputs the json that would be sent to OpenAI and writes that to the output,
* instead of actually calling OpenAI. Mostly for debugging.
*/
public static final String MODEL_OPENAIJSON = "openaijson";

/**
* Environment variable for the Anthropic API version.
*/
Expand Down Expand Up @@ -136,6 +142,9 @@ protected boolean isOpenAI() {

@Override
public String execute() {
if (MODEL_OPENAIJSON.equals(model)) {
return toJson();
}
String key = determineApiKey();
HttpClient client = HttpClient.newBuilder()
// use HTTP 1.1 since at least LM Studio doesn't handle that right. Requests are slow, anyway.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class AIGenerationTask implements Cloneable {
* A marker that can be inserted by the AI when something is wrong / unclear. We will make sure the user
* sees that by aborting.
*/
public static final String FIXME = "FIXME";
public static final String FIXME = "FIXME(GenAIPipeline)";

/**
* A pattern that matches the license header, which we want to remove to avoid clutter.
Expand Down Expand Up @@ -163,7 +163,7 @@ public AIGenerationTask setWritingStrategy(WritingStrategy strategy) {
return this;
}

public boolean hasToBeRun() {
public boolean hasToBeRun() {
List<AIInOut> allInputs = getAllInputs();
List<String> additionalMarkers = getAdditionalMarkers();
List<String> inputVersions = AIVersionMarker.calculateInputMarkers(allInputs, additionalMarkers);
Expand Down Expand Up @@ -196,7 +196,7 @@ protected List<AIInOut> getAllInputs() {
*
* @return this
*/
public AIGenerationTask addPrompt(@Nonnull AIInOut promptInput, String... placeholdersAndValues) {
public AIGenerationTask addPrompt(@Nonnull AIInOut promptInput, String... placeholdersAndValues) {
Map<String, String> map = new LinkedHashMap<>();
for (int i = 0; i < placeholdersAndValues.length; i += 2) {
map.put(placeholdersAndValues[i], placeholdersAndValues[i + 1]);
Expand All @@ -209,7 +209,7 @@ public AIGenerationTask addPrompt(@Nonnull AIInOut promptInput, String... placeh
*
* @return this
*/
public AIGenerationTask addPrompt(@Nonnull File promptInput, String... placeholdersAndValues) {
public AIGenerationTask addPrompt(@Nonnull File promptInput, String... placeholdersAndValues) {
return addPrompt(AIInOut.of(promptInput), placeholdersAndValues);
}

Expand All @@ -218,7 +218,7 @@ public AIGenerationTask addPrompt(@Nonnull File promptInput, String... placehold
*
* @return this
*/
public AIGenerationTask addPrompt(@Nonnull AIInOut promptFile, Map<String, String> placeholdersAndValues) {
public AIGenerationTask addPrompt(@Nonnull AIInOut promptFile, Map<String, String> placeholdersAndValues) {
String fileContent = promptFile.read();
if (fileContent == null) {
throw new IllegalArgumentException("Could not read prompt file " + promptFile);
Expand All @@ -243,7 +243,7 @@ public AIGenerationTask addPrompt(@Nonnull AIInOut promptFile, Map<String, Strin
*
* @return this
*/
public AIGenerationTask addPrompt(@Nonnull File promptFile, Map<String, String> placeholdersAndValues) {
public AIGenerationTask addPrompt(@Nonnull File promptFile, Map<String, String> placeholdersAndValues) {
return addPrompt(AIInOut.of(promptFile), placeholdersAndValues);
}

Expand Down Expand Up @@ -275,7 +275,7 @@ protected static String unclutter(String content) {
return content;
}

public AIGenerationTask setSystemMessage(@Nonnull AIInOut systemMessageFile) {
public AIGenerationTask setSystemMessage(@Nonnull AIInOut systemMessageFile) {
String fileContent = systemMessageFile.read();
if (fileContent == null) {
throw new IllegalArgumentException("Could not read system message file " + systemMessageFile);
Expand All @@ -287,7 +287,7 @@ public AIGenerationTask setSystemMessage(@Nonnull AIInOut systemMessageFile) {
return this;
}

public AIGenerationTask setSystemMessage(@Nonnull File systemMessageFile) {
public AIGenerationTask setSystemMessage(@Nonnull File systemMessageFile) {
return setSystemMessage(AIInOut.of(systemMessageFile));
}

Expand All @@ -302,7 +302,7 @@ protected String relativePath(@Nullable File file, @Nonnull File rootDirectory)
/**
* Execute the task if necessary. If the output file is already there and up to date, nothing is done.
*/
public AIGenerationTask execute(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory) {
public AIGenerationTask execute(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory) {
if (!hasToBeRun()) {
LOG.info(() -> "Task does not have to be run for: " + output);
return this;
Expand All @@ -327,12 +327,12 @@ public AIGenerationTask execute(@Nonnull Supplier<AIChatBuilder> chatBuilderFact
/**
* For debugging purposes: returns the JSON that would be sent to the AI.
*/
public String toJson(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory) {
public String toJson(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory) {
return makeChatBuilder(chatBuilderFactory, rootDirectory).toJson();
}

@Nonnull
protected AIChatBuilder makeChatBuilder(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory) {
protected AIChatBuilder makeChatBuilder(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory) {
requireNonNull(output, "Output file not writeable: " + output);
if ((prompt == null || prompt.isBlank()) && (systemMessage == null || systemMessage.isBlank()) && systemMessageInput == null) {
throw new IllegalArgumentException("No prompt given!");
Expand Down Expand Up @@ -378,7 +378,7 @@ protected AIChatBuilder makeChatBuilder(@Nonnull Supplier<AIChatBuilder> chatBui
*
* @return the answer of the AI - not written to a file!
*/
public String explain(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory, @Nonnull String question) {
public String explain(@Nonnull Supplier<AIChatBuilder> chatBuilderFactory, @Nonnull File rootDirectory, @Nonnull String question) {
if (hasToBeRun()) { // that's not strictly necessary, but if not that's a likely mistake
throw new IllegalStateException("Task has to be already run for: " + output);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ Do never ever give any introductory or concluding text, just the requested outpu

IMPORTANT: If something in the instructions is unclear, there is information missing, you have any questions
or after printing the response you notice that something was wrong, then append to the file a paragraph starting with
`FIXME(AI) ` and a description of that problem.
`FIXME(GenAIPipeline) ` and a description of that problem.
43 changes: 43 additions & 0 deletions examples/infileprompt/copydata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!-- AIGenPromptStart(copydata)
This uses the copy fake model to just copy the data.txt file.
AIGenCommand(copydata)
-m copy -wo -f data.txt
AIGenPromptEnd(copydata) -->
Name: Franz
Profession: Frontend developer

Name: Karl
Profession: Backend developer

Name: Maria
Profession: Designer
<!-- AIGenEnd(copydata) -->

<!-- AIGenPromptStart(openaijson)
This uses the openaijson fake model to just insert the JSON that would have been sent to OpenAI into the file.
AIGenCommand(openaijson)
-m openaijson -wo -f data.txt
AIGenPromptEnd(openaijson) -->
{
"model": "openaijson",
"messages": [
{
"role": "system",
"content": "You are an expert programming assistant who follows the users instructions exactly and to the letter.\nYou observe the rules of clean code, KISS, YAGNI, and DRY and love good documentation and well documented code.\nDo never ever give any introductory or concluding text, just the requested output, except if explicitly asked for.\n\nIMPORTANT: If something in the instructions is unclear, there is information missing, you have any questions\nor after printing the response you notice that something was wrong, then append to the file a paragraph starting with\n`FIXME(GenAIPipeline) ` and a description of that problem.\n"
},
{
"role": "user",
"content": "Retrieve the content of the input file 'data.txt'"
},
{
"role": "assistant",
"content": "Name: Franz\nProfession: Frontend developer\n\nName: Karl\nProfession: Backend developer\n\nName: Maria\nProfession: Designer\n"
},
{
"role": "user",
"content": "This uses the copy fake model to just copy the data.txt file. \n"
}
],
"temperature": 0.0,
"max_tokens": 2048
}
14 changes: 6 additions & 8 deletions examples/infileprompt/tablefromdata.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ AIGenCommand(tablefromdata)
data.txt
AIGenPromptEnd(tablefromdata) -->
---
version: AIGenVersion(16623a19, tablefromdata.md-4b2aeec1, data.txt-54cc6c01)
version: AIGenVersion(75a7c09f, tablefromdata.md-4b2aeec1, data.txt-54cc6c01)
---

| Name | Profession |
|-------|---------------------|
| Franz | Frontend developer |
| Karl | Backend developer |
| Maria | Designer |
<!-- AIGenEnd(tablefromdata) -->
End of the file
| Name | Profession |
|-------|--------------------|
| Franz | Frontend developer |
| Karl | Backend developer |
| Maria | Designer |

0 comments on commit 3c9622f

Please sign in to comment.