diff --git a/android/src/main/java/com/genexus/db/ForEachCursor.java b/android/src/main/java/com/genexus/db/ForEachCursor.java index 9f6f230b6..9f65bd7da 100644 --- a/android/src/main/java/com/genexus/db/ForEachCursor.java +++ b/android/src/main/java/com/genexus/db/ForEachCursor.java @@ -217,6 +217,7 @@ public void setTimestamp(int index, java.sql.Timestamp value) throws SQLExceptio public void setBLOBFile(int index, String fileName) throws SQLException {} public void setBLOBFile(int index, String fileName, boolean isMultiMedia) throws SQLException {} public void setBLOBFile(int index, String fileName, boolean isMultiMedia, boolean downloadContent) throws SQLException {} + public void setEmbedding(int index, Float[] value) throws SQLException {} public void setVarchar(int index, String value, int length, boolean allowsNull) throws SQLException {} public void setLongVarchar(int index, String value, boolean allowsNull) throws SQLException {} diff --git a/android/src/main/java/com/genexus/db/driver/GXCallableStatement.java b/android/src/main/java/com/genexus/db/driver/GXCallableStatement.java index a068a36ea..ea677b6eb 100644 --- a/android/src/main/java/com/genexus/db/driver/GXCallableStatement.java +++ b/android/src/main/java/com/genexus/db/driver/GXCallableStatement.java @@ -651,6 +651,11 @@ public byte[] getBytes(int columnIndex) throws SQLException return stmt.getBytes(columnIndex); } + public Float[] getGxembedding (int columnIndex) throws SQLException + { + return null; + } + public java.util.UUID getGUID(int columnIndex) throws SQLException { if (DEBUG) diff --git a/android/src/main/java/com/genexus/db/driver/GXPreparedStatement.java b/android/src/main/java/com/genexus/db/driver/GXPreparedStatement.java index 38a78e4e6..44df48d60 100644 --- a/android/src/main/java/com/genexus/db/driver/GXPreparedStatement.java +++ b/android/src/main/java/com/genexus/db/driver/GXPreparedStatement.java @@ -1307,7 +1307,9 @@ else if (file.exists() && !fileNameNew.startsWith(blobBasePath)) } } } - + + public void setEmbedding(int index, Float[] value) throws SQLException{ + } public void setBinaryStream(int index, java.io.InputStream value, int length) throws SQLException { if (DEBUG) diff --git a/android/src/main/java/com/genexus/db/driver/GXResultSet.java b/android/src/main/java/com/genexus/db/driver/GXResultSet.java index 931b9c3a2..04b706525 100644 --- a/android/src/main/java/com/genexus/db/driver/GXResultSet.java +++ b/android/src/main/java/com/genexus/db/driver/GXResultSet.java @@ -770,6 +770,11 @@ public byte[] getBytes(int columnIndex) throws SQLException return result.getBytes(columnIndex); } + public Float[] getGxembedding (int columnIndex) throws SQLException + { + return null; + } + public java.util.UUID getGUID(int columnIndex) throws SQLException { java.util.UUID value; diff --git a/common/src/main/java/com/genexus/GXExternalCollection.java b/common/src/main/java/com/genexus/GXExternalCollection.java index de7795165..baf60e363 100644 --- a/common/src/main/java/com/genexus/GXExternalCollection.java +++ b/common/src/main/java/com/genexus/GXExternalCollection.java @@ -98,6 +98,22 @@ public Vector getStruct() } return struct; } - + + public ArrayList getExternalInstance() { + ArrayList list = new ArrayList(); + for (T Item : this) + { + try + { + list.add(Item.getClass().getMethod("getExternalInstance", new Class[]{}).invoke(Item)); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + return list; + } + } diff --git a/common/src/main/java/com/genexus/db/BufferIFieldGetter.java b/common/src/main/java/com/genexus/db/BufferIFieldGetter.java index dc1b6b00f..d310cd713 100644 --- a/common/src/main/java/com/genexus/db/BufferIFieldGetter.java +++ b/common/src/main/java/com/genexus/db/BufferIFieldGetter.java @@ -85,6 +85,10 @@ public byte[] getBytes(int columnIndex) throws SQLException { return ((byte[]) value[columnIndex - 1]); } + public Float[] getGxembedding(int columnIndex) throws SQLException { + return ((Float[]) value[columnIndex - 1]); + } + public java.sql.Date getDate(int columnIndex) throws SQLException { return ((java.sql.Date) value[columnIndex - 1]); } diff --git a/common/src/main/java/com/genexus/db/CachedIFieldGetter.java b/common/src/main/java/com/genexus/db/CachedIFieldGetter.java index 80cf0e7f7..e112653bc 100644 --- a/common/src/main/java/com/genexus/db/CachedIFieldGetter.java +++ b/common/src/main/java/com/genexus/db/CachedIFieldGetter.java @@ -220,6 +220,11 @@ public byte[] getBytes(int columnIndex) throws SQLException else return ((byte[][])value[index])[0]; } + + public Float[] getGxembedding(int columnIndex) throws SQLException + { + return ((Float[][])value[getColumnIndex(columnIndex)])[0]; + } public java.sql.Date getDate(int columnIndex) throws SQLException { diff --git a/common/src/main/java/com/genexus/db/IFieldGetter.java b/common/src/main/java/com/genexus/db/IFieldGetter.java index e0a842811..810d5dec1 100644 --- a/common/src/main/java/com/genexus/db/IFieldGetter.java +++ b/common/src/main/java/com/genexus/db/IFieldGetter.java @@ -33,4 +33,5 @@ public interface IFieldGetter String getMultimediaUri(int columnIndex) throws SQLException; String getMultimediaUri(int columnIndex, boolean absPath) throws SQLException; java.util.UUID getGUID(int columnIndex) throws SQLException; + Float[] getGxembedding(int columnIndex) throws SQLException; } diff --git a/common/src/main/java/com/genexus/db/IFieldSetter.java b/common/src/main/java/com/genexus/db/IFieldSetter.java index 0eafd787e..05ddd5557 100644 --- a/common/src/main/java/com/genexus/db/IFieldSetter.java +++ b/common/src/main/java/com/genexus/db/IFieldSetter.java @@ -35,6 +35,7 @@ public interface IFieldSetter public void setBLOBFile(int index, String fileName) throws SQLException; public void setBLOBFile(int index, String fileName, boolean isMultiMedia) throws SQLException; public void setBLOBFile(int index, String fileName, boolean isMultiMedia, boolean downloadContent) throws SQLException; + public void setEmbedding(int index, Float[] value) throws SQLException; public void setVarchar(int index, String value, int length, boolean allowsNull) throws SQLException; public void setLongVarchar(int index, String value, boolean allowsNull) throws SQLException; diff --git a/common/src/main/java/com/genexus/util/CallResult.java b/common/src/main/java/com/genexus/util/CallResult.java new file mode 100644 index 000000000..95894760c --- /dev/null +++ b/common/src/main/java/com/genexus/util/CallResult.java @@ -0,0 +1,30 @@ +package com.genexus.util; + +import com.genexus.GXBaseCollection; +import com.genexus.SdtMessages_Message; + +public class CallResult { + private boolean success = true; + private boolean fail; + private final GXBaseCollection messages = new GXBaseCollection<>(); + + public boolean success() { + return success; + } + + public void setFail() { + fail = true; + success =false; + } + + public boolean fail() { + return fail; + } + + public void addMessage(SdtMessages_Message message) { + messages.add(message); + } + public GXBaseCollection getMessages() { + return messages; + } +} diff --git a/common/src/main/java/com/genexus/util/GXProperties.java b/common/src/main/java/com/genexus/util/GXProperties.java index 40e7ff440..b5cc8d3c5 100644 --- a/common/src/main/java/com/genexus/util/GXProperties.java +++ b/common/src/main/java/com/genexus/util/GXProperties.java @@ -1,6 +1,6 @@ package com.genexus.util; -import java.util.LinkedHashMap; +import java.util.*; import com.genexus.internet.IGxJSONSerializable; @@ -10,8 +10,6 @@ import com.genexus.CommonUtil; import com.genexus.SdtMessages_Message; import com.genexus.GXBaseCollection; -import java.util.Iterator; -import java.util.Map; public class GXProperties implements IGxJSONSerializable { private LinkedHashMap < String, GXProperty > properties = new LinkedHashMap < > (); @@ -118,6 +116,16 @@ public String toJSonString() { return jObj.toString(); } + public ArrayList getList() { + ArrayList list = new ArrayList<>(); + int i = 0; + while (count() > i) { + list.add(item(i)); + i++; + } + return list; + } + public boolean fromJSonString(String s) { return fromJSonString(s, null); } diff --git a/java/client.cfg b/java/client.cfg index 4866b57ed..3b23c517a 100644 --- a/java/client.cfg +++ b/java/client.cfg @@ -47,6 +47,8 @@ ORQ_CLIENT_URL= ORQ_SERVER_DIR= TMPMEDIA_DIR=PrivateTempStorage PRINT_LAYOUT_METADATA_DIR=LayoutMetadata +AI_PROVIDER=https://api.saia.ai/chat +AI_PROVIDER_API_KEY=xxx HTTP_PROTOCOL=Unsecure SAMESITE_COOKIE=Lax StorageTimeZone= 1 diff --git a/java/src/main/java/com/genexus/GXProcedure.java b/java/src/main/java/com/genexus/GXProcedure.java index 2185b0840..d2b65dfd2 100644 --- a/java/src/main/java/com/genexus/GXProcedure.java +++ b/java/src/main/java/com/genexus/GXProcedure.java @@ -2,17 +2,24 @@ package com.genexus; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Date; + +import com.fasterxml.jackson.databind.ObjectMapper; import com.genexus.db.Namespace; import com.genexus.db.UserInformation; import com.genexus.diagnostics.GXDebugInfo; import com.genexus.diagnostics.GXDebugManager; +import com.genexus.internet.HttpClient; import com.genexus.internet.HttpContext; import com.genexus.mock.GXMockProvider; import com.genexus.performance.ProcedureInfo; import com.genexus.performance.ProceduresInfo; -import com.genexus.util.ReorgSubmitThreadPool; -import com.genexus.util.SubmitThreadPool; +import com.genexus.util.*; +import com.genexus.util.saia.OpenAIRequest; +import com.genexus.util.saia.OpenAIResponse; +import com.genexus.util.saia.SaiaService; +import org.json.JSONObject; public abstract class GXProcedure implements IErrorHandler, ISubmitteable { public abstract void initialize(); @@ -28,7 +35,8 @@ public abstract class GXProcedure implements IErrorHandler, ISubmitteable { UserInformation ui=null; private Date beginExecute; - + private HttpClient client; + public static final int IN_NEW_UTL = -2; public GXProcedure(int remoteHandle, ModelContext context, String location) { @@ -257,4 +265,70 @@ protected void mockExecute() { } privateExecute( ); } + + protected String callTool(String name, String arguments) { + return ""; + } + + protected String callAssistant(String agent, GXProperties properties, ArrayList messages, CallResult result) { + return callAgent(agent, properties, messages, result); + } + + protected ChatResult chatAgent(String agent, GXProperties properties, ArrayList messages, CallResult result) { + callAgent(agent, true, properties, messages, result); + return new ChatResult(this, agent, properties, messages, result, client); + } + + protected String callAgent(String agent, GXProperties properties, ArrayList messages, CallResult result) { + return callAgent(agent, false, properties, messages, result); + } + + protected String callAgent(String agent, boolean stream, GXProperties properties, ArrayList messages, CallResult result) { + OpenAIRequest aiRequest = new OpenAIRequest(); + aiRequest.setModel(String.format("saia:agent:%s", agent)); + if (!messages.isEmpty()) + aiRequest.setMessages(messages); + aiRequest.setVariables(properties.getList()); + if (stream) + aiRequest.setStream(true); + client = new HttpClient(); + OpenAIResponse aiResponse = SaiaService.call(aiRequest, client, result); + if (aiResponse != null && aiResponse.getChoices() != null) { + for (OpenAIResponse.Choice element : aiResponse.getChoices()) { + String finishReason = element.getFinishReason(); + if (finishReason.equals("stop")) + return element.getMessage().getStringContent(); + if (finishReason.equals("tool_calls")) { + messages.add(element.getMessage()); + return processNotChunkedResponse(agent, stream, properties, messages, result, element.getMessage().getToolCalls()); + } + } + } else if (client.getStatusCode() == 200) { + return ""; + } + return ""; + } + + public String processNotChunkedResponse(String agent, boolean stream, GXProperties properties, ArrayList messages, CallResult result, ArrayList toolCalls) { + for (OpenAIResponse.ToolCall tollCall : toolCalls) { + processToolCall(tollCall, messages); + } + return callAgent(agent, stream, properties, messages, result); + } + + private void processToolCall(OpenAIResponse.ToolCall toolCall, ArrayList messages) { + String result; + String functionName = toolCall.getFunction().getName(); + try { + result = callTool(functionName, toolCall.getFunction().getArguments()); + } + catch (Throwable e) { + result = String.format("Error calling tool %s", functionName); + } + OpenAIResponse.Message toolCallMessage = new OpenAIResponse.Message(); + toolCallMessage.setRole("tool"); + toolCallMessage.setStringContent(result); + toolCallMessage.setToolCallId(toolCall.getId()); + messages.add(toolCallMessage); + } } diff --git a/java/src/main/java/com/genexus/GXutil.java b/java/src/main/java/com/genexus/GXutil.java index 9617ff01a..651132125 100644 --- a/java/src/main/java/com/genexus/GXutil.java +++ b/java/src/main/java/com/genexus/GXutil.java @@ -9,6 +9,7 @@ import com.genexus.common.interfaces.SpecificImplementation; import com.genexus.db.DataStoreProvider; +import com.genexus.db.GXEmbedding; import com.genexus.internet.HttpContext; import com.genexus.internet.StringCollection; import com.genexus.platform.INativeFunctions; @@ -1777,4 +1778,8 @@ public static String buildURLFromHttpClient(com.genexus.internet.HttpClient GXSo return url.toString(); } + public static String embeddingToStr(GXEmbedding embedding) { + return embedding.toString(); + } + } diff --git a/java/src/main/java/com/genexus/db/ForEachCursor.java b/java/src/main/java/com/genexus/db/ForEachCursor.java index 328b14b87..274aa9be6 100644 --- a/java/src/main/java/com/genexus/db/ForEachCursor.java +++ b/java/src/main/java/com/genexus/db/ForEachCursor.java @@ -327,6 +327,7 @@ public void setTimestamp(int index, java.sql.Timestamp value) throws SQLExceptio public void setBLOBFile(int index, String fileName) throws SQLException {} public void setBLOBFile(int index, String fileName, boolean isMultiMedia) throws SQLException {} public void setBLOBFile(int index, String fileName, boolean isMultiMedia, boolean downloadContet) throws SQLException {} + public void setEmbedding(int index, Float[] value) throws SQLException {} public void setVarchar(int index, String value, int length, boolean allowsNull) throws SQLException {} public void setLongVarchar(int index, String value, boolean allowsNull) throws SQLException {} @@ -339,6 +340,8 @@ public void setParameterRT(String name, String value) boolean isLike = false; if(value.equals("like")) isLike = true; + else if (value.equals("Distance")) + value = ds.getDistanceFunction(); else if(!value.equals("=") && !value.equals(">") && !value.equals(">=") && !value.equals("<=") && !value.equals("<") && !value.equals("<>")) { diff --git a/java/src/main/java/com/genexus/db/GXEmbedding.java b/java/src/main/java/com/genexus/db/GXEmbedding.java new file mode 100644 index 000000000..d935dffac --- /dev/null +++ b/java/src/main/java/com/genexus/db/GXEmbedding.java @@ -0,0 +1,99 @@ +package com.genexus.db; + +import com.genexus.CommonUtil; +import com.genexus.GXBaseCollection; +import com.genexus.SdtMessages_Message; +import com.genexus.util.CallResult; +import com.genexus.util.saia.OpenAIRequest; +import com.genexus.util.saia.OpenAIResponse; +import com.genexus.util.saia.SaiaService; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class GXEmbedding { + + private String model; + private int dimensions; + private List embedding; + + public GXEmbedding() { + embedding = new ArrayList<>(); + } + + public GXEmbedding(String model, int dimensions) { + this.model = model; + this.dimensions = dimensions; + embedding = new ArrayList<>(Collections.nCopies(dimensions, 0.0f)); + } + + public GXEmbedding(Float[] embedding, String model, int dimensions) { + this.model = model; + this.dimensions = dimensions; + this.embedding = Arrays.asList(embedding); + } + + public GXEmbedding(List embedding) { + this.embedding = embedding; + } + + public String getModel() { + return model; + } + + public int getDimensions() { + return dimensions; + } + + public void setEmbedding(List embedding) { + if (!embedding.isEmpty()) + this.embedding = embedding; + } + + public Float[] getFloatArray() { + return embedding.toArray(new Float[0]); + } + + public static GXEmbedding generateEmbedding(GXEmbedding embeddingInfo, String text, GXBaseCollection Messages) { + try { + List embedding = getEmbedding(embeddingInfo.getModel(), embeddingInfo.getDimensions(), text); + embeddingInfo.setEmbedding(embedding); + } + catch (Exception ex) { + CommonUtil.ErrorToMessages("GenerateEmbedding Error", ex.getMessage(), Messages); + } + return embeddingInfo; + } + + public static List getEmbedding(String model, int dimensions, String input) { + if (input.isEmpty()) + return new ArrayList<>(); + ArrayList inputList = new ArrayList<>(); + inputList.add(input); + return getEmbedding(model, dimensions, inputList); + } + + public static List getEmbedding(String model, int dimensions, ArrayList inputList) { + OpenAIRequest aiRequest = new OpenAIRequest(); + aiRequest.setModel(model); + aiRequest.setInput(inputList); + aiRequest.setDimension(dimensions); + OpenAIResponse aiResponse = SaiaService.call(aiRequest, true, new CallResult()); + if (aiResponse != null) + return aiResponse.getData().get(0).getEmbedding().stream() + .map(Double::floatValue) + .collect(Collectors.toList()); + + return new ArrayList<>(); + } + + public String toString() + { + return embedding.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + } +} diff --git a/java/src/main/java/com/genexus/db/driver/DataSource.java b/java/src/main/java/com/genexus/db/driver/DataSource.java index d45f9ea86..b8548928d 100644 --- a/java/src/main/java/com/genexus/db/driver/DataSource.java +++ b/java/src/main/java/com/genexus/db/driver/DataSource.java @@ -71,6 +71,7 @@ public class DataSource extends AbstractDataSource public String jdbcDataSource; private String namespace; + private String dbmsVersion; public DataSource( String name, @@ -581,7 +582,15 @@ public DataSource copy() copyDataSource.setConnectionPools(this.getConnectionPools()); return copyDataSource; } - + + public String getDistanceFunction() { + String distanceFunction = "DISTANCE"; + if (dbms.getId() == GXDBMS.DBMS_MYSQL && getDbmsVersion() != null && getDbmsVersion().contains("MariaDB")) { + distanceFunction = "vec_distance_cosine"; + } + return distanceFunction; + } + public String[] concatOp() { switch(dbms.getId()) @@ -616,7 +625,15 @@ public void RWPoolRecycle() { if (getConnectionPool().getRWConnectionPool(defaultUser) != null) getConnectionPool().getRWConnectionPool(defaultUser).PoolRecycle(); - } + } + + public void setDbmsVersion(String dbmsVersion) { + this.dbmsVersion = dbmsVersion; + } + + public String getDbmsVersion() { + return dbmsVersion; + } } diff --git a/java/src/main/java/com/genexus/db/driver/GXCallableStatement.java b/java/src/main/java/com/genexus/db/driver/GXCallableStatement.java index c580d639f..77d8c515c 100644 --- a/java/src/main/java/com/genexus/db/driver/GXCallableStatement.java +++ b/java/src/main/java/com/genexus/db/driver/GXCallableStatement.java @@ -704,6 +704,14 @@ public byte[] getBytes(int columnIndex) throws SQLException return stmt.getBytes(columnIndex); } + public Float[] getGxembedding (int columnIndex) throws SQLException + { + if (DEBUG ) + log(GXDBDebug.LOG_MAX, "Warning: getEmbedding"); + + return(Float[]) stmt.getArray(columnIndex).getArray(); + } + public java.util.UUID getGUID(int columnIndex) throws SQLException { if (DEBUG) diff --git a/java/src/main/java/com/genexus/db/driver/GXConnection.java b/java/src/main/java/com/genexus/db/driver/GXConnection.java index 85da9eb00..ce3c7ddc0 100644 --- a/java/src/main/java/com/genexus/db/driver/GXConnection.java +++ b/java/src/main/java/com/genexus/db/driver/GXConnection.java @@ -190,6 +190,7 @@ public GXConnection( ModelContext context, int handle, String user, String passw try { version = dma.getDatabaseProductVersion(); + dataSource.setDbmsVersion(version); } catch (SQLException e) { @@ -1744,8 +1745,7 @@ public void setClientInfo(String arg0, String arg1) public Array createArrayOf(String typeName, Object[] elements) throws SQLException { - // TODO Auto-generated method stub - return null; + return getJDBCConnection().createArrayOf(typeName, elements); } public void abort(Executor executor) diff --git a/java/src/main/java/com/genexus/db/driver/GXPreparedStatement.java b/java/src/main/java/com/genexus/db/driver/GXPreparedStatement.java index 4bd757dba..01ba0cb81 100644 --- a/java/src/main/java/com/genexus/db/driver/GXPreparedStatement.java +++ b/java/src/main/java/com/genexus/db/driver/GXPreparedStatement.java @@ -13,6 +13,7 @@ import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URL; +import java.nio.ByteBuffer; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -1506,6 +1507,51 @@ else if(blobFiles.length < index) } } + private static byte[] floatArrayToByteArray(Float[] floats) { + ByteBuffer buffer = ByteBuffer.allocate(floats.length * Float.BYTES); + + for (Float f : floats) { + if (f != null) { + buffer.putFloat(f); + } else { + buffer.putFloat(0.0f); + } + } + return buffer.array(); + } + + public void setEmbedding(int index, Float[] value) throws SQLException{ + byte[] bytes = null; + Array sqlArray = null; + if (con.getDBMS().getId() == GXDBMS.DBMS_POSTGRESQL) + sqlArray = con.createArrayOf("float4", value); + else + bytes = floatArrayToByteArray(value); + if (DEBUG) + { + log(GXDBDebug.LOG_MAX, "setEmbedding - index : " + index); + try + { + if (con.getDBMS().getId() == GXDBMS.DBMS_POSTGRESQL) + stmt.setArray(index, sqlArray); + else + stmt.setBytes(index, bytes); + } + catch (SQLException sqlException) + { + if (con.isLogEnabled()) con.logSQLException(con.getHandle(), sqlException); + throw sqlException; + } + } + else + { + if (con.getDBMS().getId() == GXDBMS.DBMS_POSTGRESQL) + stmt.setArray(index, sqlArray); + else + stmt.setBytes(index, bytes); + } + } + public void setBinaryStream(int index, java.io.InputStream value, int length) throws SQLException { if (DEBUG) diff --git a/java/src/main/java/com/genexus/db/driver/GXResultSet.java b/java/src/main/java/com/genexus/db/driver/GXResultSet.java index b59803f84..166703275 100644 --- a/java/src/main/java/com/genexus/db/driver/GXResultSet.java +++ b/java/src/main/java/com/genexus/db/driver/GXResultSet.java @@ -9,6 +9,7 @@ import java.io.OutputStream; import java.io.Reader; import java.math.BigDecimal; +import java.nio.ByteBuffer; import java.sql.Array; import java.sql.Blob; import java.sql.Clob; @@ -831,6 +832,38 @@ public byte[] getBytes(int columnIndex) throws SQLException return result.getBytes(columnIndex); } + public Float[] getGxembedding (int columnIndex) throws SQLException + { + if (DEBUG ) + log(GXDBDebug.LOG_MAX, "Warning: getEmbedding"); + + if (con.getDBMS().getId() == GXDBMS.DBMS_POSTGRESQL) + return convertVectorStringToFloatArray(result.getArray(columnIndex).toString()); + else + return byteArrayToFloatObjectArray(result.getBytes(columnIndex)); + } + + private static Float[] byteArrayToFloatObjectArray(byte[] bytes) { + Float[] floats = new Float[bytes.length / Float.BYTES]; + + ByteBuffer buffer = ByteBuffer.wrap(bytes); + for (int i = 0; i < floats.length; i++) { + floats[i] = buffer.getFloat(); + } + return floats; + } + + private static Float[] convertVectorStringToFloatArray(String vectorString) { + vectorString = vectorString.replace("[", "").replace("]", "").trim(); + String[] stringValues = vectorString.split(","); + Float[] floatArray = new Float[stringValues.length]; + + for (int i = 0; i < stringValues.length; i++) { + floatArray[i] = Float.parseFloat(stringValues[i].trim()); + } + return floatArray; + } + public java.util.UUID getGUID(int columnIndex) throws SQLException { java.util.UUID value; diff --git a/java/src/main/java/com/genexus/util/ChatResult.java b/java/src/main/java/com/genexus/util/ChatResult.java new file mode 100644 index 000000000..14eabe0ee --- /dev/null +++ b/java/src/main/java/com/genexus/util/ChatResult.java @@ -0,0 +1,58 @@ +package com.genexus.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.genexus.GXProcedure; +import com.genexus.internet.HttpClient; +import com.genexus.util.saia.OpenAIResponse; +import org.json.JSONObject; + +import java.util.ArrayList; + +public class ChatResult { + private HttpClient client = null; + private String agent = null; + private GXProperties properties = null; + private ArrayList messages = null; + private CallResult result = null; + private GXProcedure agentProcedure = null; + + public ChatResult() { + } + + public ChatResult(GXProcedure agentProcedure, String agent, GXProperties properties, ArrayList messages, CallResult result, HttpClient client) { + this.agentProcedure = agentProcedure; + this.agent = agent; + this.properties = properties; + this.messages = messages; + this.result = result; + this.client = client; + } + + public boolean hasMoreData() { + return !client.getEof(); + } + + public String getMoreData() { + String data = client.readChunk(); + if (data.isEmpty()) + return ""; + int index = data.indexOf("data:") + "data:".length(); + String chunkJson = data.substring(index).trim(); + try { + JSONObject jsonResponse = new JSONObject(chunkJson); + OpenAIResponse chunkResponse = new ObjectMapper().readValue(jsonResponse.toString(), OpenAIResponse.class); + OpenAIResponse.Choice choise = chunkResponse.getChoices().get(0); + if (choise.getFinishReason() != null && choise.getFinishReason().equals("tool_calls") && agent != null) { + messages.add(choise.getMessage()); + return agentProcedure.processNotChunkedResponse(agent, true, properties, messages, result, choise.getMessage().getToolCalls()); + } + String chunkString = choise.getDelta().getStringContent(); + if (chunkString == null) + return ""; + return chunkString; + } + catch (Exception e) { + return ""; + } + } +} diff --git a/java/src/main/java/com/genexus/util/saia/ContentDeserializer.java b/java/src/main/java/com/genexus/util/saia/ContentDeserializer.java new file mode 100644 index 000000000..d32fbc47c --- /dev/null +++ b/java/src/main/java/com/genexus/util/saia/ContentDeserializer.java @@ -0,0 +1,33 @@ +package com.genexus.util.saia; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; +import java.util.ArrayList; + +class ContentDeserializer extends JsonDeserializer { + public ContentDeserializer() {} + + @Override + public OpenAIResponse.Content deserialize(JsonParser p, DeserializationContext ct) throws IOException { + ObjectCodec codec = p.getCodec(); + JsonNode node = codec.readTree(p); + + if (node.isTextual()) { + return new OpenAIResponse.StringContent(node.asText()); + } else if (node.isArray()) { + ArrayList items = new ArrayList<>(); + for (JsonNode itemNode : node) { + OpenAIResponse.StructuredContentItem item = codec.treeToValue(itemNode, OpenAIResponse.StructuredContentItem.class); + items.add(item); + } + return new OpenAIResponse.StructuredContent(items); + } + + throw new IOException("Invalid content format"); + } +} diff --git a/java/src/main/java/com/genexus/util/saia/ContentSerializer.java b/java/src/main/java/com/genexus/util/saia/ContentSerializer.java new file mode 100644 index 000000000..665a9c380 --- /dev/null +++ b/java/src/main/java/com/genexus/util/saia/ContentSerializer.java @@ -0,0 +1,30 @@ +package com.genexus.util.saia; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +class ContentSerializer extends JsonSerializer { + public ContentSerializer() { + } + + @Override + public void serialize(OpenAIResponse.Content value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + ObjectMapper mapper = (ObjectMapper) gen.getCodec(); + + if (value instanceof OpenAIResponse.StringContent) { + gen.writeString(((OpenAIResponse.StringContent) value).getValue()); + } else if (value instanceof OpenAIResponse.StructuredContent) { + gen.writeStartArray(); + for (OpenAIResponse.StructuredContentItem item : ((OpenAIResponse.StructuredContent) value).getItems()) { + mapper.writeValue(gen, item); + } + gen.writeEndArray(); + } else { + gen.writeNull(); + } + } +} diff --git a/java/src/main/java/com/genexus/util/saia/OpenAIRequest.java b/java/src/main/java/com/genexus/util/saia/OpenAIRequest.java new file mode 100644 index 000000000..59a7bc866 --- /dev/null +++ b/java/src/main/java/com/genexus/util/saia/OpenAIRequest.java @@ -0,0 +1,91 @@ +package com.genexus.util.saia; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.genexus.util.GXProperty; + +import java.util.ArrayList; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class OpenAIRequest { + + @JsonProperty("model") + private String model; + + @JsonProperty("messages") + private ArrayList messages; + + @JsonProperty("prompt") + private String prompt; + + @JsonProperty("input") + private ArrayList input; + + @JsonProperty("max_tokens") + private Integer maxTokens; + + @JsonProperty("temperature") + private Double temperature; + + @JsonProperty("stream") + private Boolean stream; + + @JsonProperty("stop") + private ArrayList stop; + + @JsonProperty("presence_penalty") + private Double presencePenalty; + + @JsonProperty("frequency_penalty") + private Double frequencyPenalty; + + @JsonProperty("user") + private String user; + + @JsonProperty("variables") + private ArrayList variables; + + @JsonProperty("dimensions") + private int dimension; + + public String getModel() { return model; } + public void setModel(String model) { this.model = model; } + + public ArrayList getMessages() { return messages; } + public void setMessages(ArrayList messages) { this.messages = messages; } + + public String getPrompt() { return prompt; } + public void setPrompt(String prompt) { this.prompt = prompt; } + + public ArrayList getInput() { return input; } + public void setInput(ArrayList input) { this.input = input; } + + public Integer getMaxTokens() { return maxTokens; } + public void setMaxTokens(Integer maxTokens) { this.maxTokens = maxTokens; } + + public Double getTemperature() { return temperature; } + public void setTemperature(Double temperature) { this.temperature = temperature; } + + public Boolean getStream() { return stream; } + public void setStream(Boolean stream) { this.stream = stream; } + + public ArrayList getStop() { return stop; } + public void setStop(ArrayList stop) { this.stop = stop; } + + public Double getPresencePenalty() { return presencePenalty; } + public void setPresencePenalty(Double presencePenalty) { this.presencePenalty = presencePenalty; } + + public Double getFrequencyPenalty() { return frequencyPenalty; } + public void setFrequencyPenalty(Double frequencyPenalty) { this.frequencyPenalty = frequencyPenalty; } + + public String getUser() { return user; } + public void setUser(String user) { this.user = user; } + + public ArrayList getVariables() { return variables; } + public void setVariables(ArrayList variables) { this.variables = variables; } + + public int getDimension() { return dimension; } + public void setDimension(int dimension) { this.dimension = dimension; } +} \ No newline at end of file diff --git a/java/src/main/java/com/genexus/util/saia/OpenAIResponse.java b/java/src/main/java/com/genexus/util/saia/OpenAIResponse.java new file mode 100644 index 000000000..27a7a0899 --- /dev/null +++ b/java/src/main/java/com/genexus/util/saia/OpenAIResponse.java @@ -0,0 +1,299 @@ +package com.genexus.util.saia; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; + +import java.util.ArrayList; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class OpenAIResponse { + + @JsonProperty("id") + private String id; + + @JsonProperty("object") + private String object; + + @JsonProperty("created") + private long created; + + @JsonProperty("choices") + private ArrayList choices; + + @JsonProperty("usage") + private Usage usage; + + @JsonProperty("tool_calls") + private ArrayList tool_calls; + + @JsonProperty("data") + private ArrayList data; + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + + public String getObject() { return object; } + public void setObject(String object) { this.object = object; } + + public long getCreated() { return created; } + public void setCreated(long created) { this.created = created; } + + public ArrayList getChoices() { return choices; } + public void setChoices(ArrayList choices) { this.choices = choices; } + + public Usage getUsage() { return usage; } + public void setUsage(Usage usage) { this.usage = usage; } + + public ArrayList getToolCalls() { return tool_calls; } + public void setToolCalls(ArrayList tool_calls) { this.tool_calls = tool_calls; } + + public ArrayList getData() { return data; } + public void setData(ArrayList data) { this.data = data; } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Choice { + + @JsonProperty("index") + private int index; + + @JsonProperty("message") + private Message message; + + @JsonProperty("delta") + private Message delta; + + @JsonProperty("finish_reason") + private String finishReason; + + public int getIndex() { return index; } + public void setIndex(int index) { this.index = index; } + + public Message getMessage() { return message; } + public void setMessage(Message message) { this.message = message; } + + public Message getDelta() { return message; } + public void setDelta(Message message) { this.message = message; } + + public String getFinishReason() { return finishReason; } + public void setFinishReason(String finishReason) { this.finishReason = finishReason; } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Message { + + @JsonProperty("role") + private String role; + + @JsonProperty("content") + @JsonSerialize(using = ContentSerializer.class) + @JsonDeserialize(using = ContentDeserializer.class) + private Content content; + + @JsonProperty("tool_calls") + private ArrayList toolCalls; + + @JsonProperty("tool_call_id") + private String toolCallId; + + public String getRole() { return role; } + public void setRole(String role) { this.role = role; } + + @JsonIgnore + public String getStringContent() { return ((StringContent) content).getValue(); } + @JsonIgnore + public StructuredContent getStructuredContent() {return (StructuredContent)content;} + public Content getContent() { return content; } + @JsonIgnore + public void setStringContent(String content) { this.content = new StringContent(content); } + @JsonIgnore + public void setStructuredContent(StructuredContent content) { this.content = content; } + public void setContent(Content content) { this.content = content; } + + public ArrayList getToolCalls() { return toolCalls; } + public void setToolCalls(ArrayList toolCalls) { this.toolCalls = toolCalls; } + + public String getToolCallId() { return toolCallId; } + public void setToolCallId(String toolCallId) { this.toolCallId = toolCallId; } + } + + public interface Content { } + + public static class StringContent implements Content { + public StringContent() {} + + private String value; + + public StringContent(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public static class StructuredContent implements Content { + public StructuredContent() {} + + private ArrayList items; + + public StructuredContent(ArrayList items) { + this.items = items; + } + + public ArrayList getItems() { + return items; + } + + public void setItems(ArrayList items) { + this.items = items; + } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class StructuredContentItem { + public StructuredContentItem() {} + + private String type; + private String text; + private ImageUrl image_url; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public ImageUrl getImage_url() { + return image_url; + } + + public void setImage_url(ImageUrl imageURL) { + this.image_url = imageURL; + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class ImageUrl { + public ImageUrl() {} + + private String url; + private String detail; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getDetail() { + return detail; + } + + public void setDetail(String detail) { + this.detail = detail; + } + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class ToolCall { + + @JsonProperty("id") + private String id; + + @JsonProperty("type") + private String type; + + @JsonProperty("function") + private Function function; + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + + public String getType() { return type; } + public void setType(String type) { this.type = type; } + + public Function getFunction() { return function; } + public void setFunction(Function function) { this.function = function; } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Function { + + @JsonProperty("name") + private String name; + + @JsonProperty("arguments") + private String arguments; + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getArguments() { return arguments; } + public void setArguments(String arguments) { this.arguments = arguments; } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class Usage { + + @JsonProperty("prompt_tokens") + private int promptTokens; + + @JsonProperty("completion_tokens") + private int completionTokens; + + @JsonProperty("total_tokens") + private int totalTokens; + + public int getPromptTokens() { return promptTokens; } + public void setPromptTokens(int promptTokens) { this.promptTokens = promptTokens; } + + public int getCompletionTokens() { return completionTokens; } + public void setCompletionTokens(int completionTokens) { this.completionTokens = completionTokens; } + + public int getTotalTokens() { return totalTokens; } + public void setTotalTokens(int totalTokens) { this.totalTokens = totalTokens; } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DataItem { + + @JsonProperty("id") + private String id; + + @JsonProperty("object") + private String object; + + @JsonProperty("embedding") + private ArrayList embedding; + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + + public String getObject() { return object; } + public void setObject(String object) { this.object = object; } + + public ArrayList getEmbedding() { return embedding; } + public void setEmbedding(ArrayList embedding) { this.embedding = embedding; } + } +} diff --git a/java/src/main/java/com/genexus/util/saia/SaiaService.java b/java/src/main/java/com/genexus/util/saia/SaiaService.java new file mode 100644 index 000000000..30230b281 --- /dev/null +++ b/java/src/main/java/com/genexus/util/saia/SaiaService.java @@ -0,0 +1,81 @@ +package com.genexus.util.saia; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.genexus.SdtMessages_Message; +import com.genexus.common.interfaces.SpecificImplementation; +import com.genexus.diagnostics.core.ILogger; +import com.genexus.diagnostics.core.LogManager; +import com.genexus.internet.HttpClient; +import org.json.JSONObject; +import com.genexus.util.CallResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SaiaService { + private static final ILogger logger = LogManager.getLogger(SaiaService.class); + private static final String apiKey = (String) SpecificImplementation.Application.getProperty("AI_PROVIDER_API_KEY", "");; + private static final String aiProvider = (String) SpecificImplementation.Application.getProperty("AI_PROVIDER", ""); + private static final Logger log = LoggerFactory.getLogger(SaiaService.class); + + public static OpenAIResponse call(OpenAIRequest request, HttpClient client, CallResult result) { + return call(request, false, client, result); + } + + public static OpenAIResponse call(OpenAIRequest request, boolean isEmbedding, CallResult result) { + return call(request, isEmbedding, new HttpClient(), result); + } + + public static OpenAIResponse call(OpenAIRequest request, boolean isEmbedding, HttpClient client, CallResult result) { + try { + String jsonRequest = new ObjectMapper().writeValueAsString(request); + logger.debug("Agent payload: " + jsonRequest); + String providerURL = aiProvider + "/chat";; + + client.setSecure(1); + client.addHeader("Content-Type", "application/json"); + client.addHeader("Authorization", "Bearer " + apiKey); + if (isEmbedding) { + client.addHeader("X-Saia-Source", "Embedding"); + providerURL = providerURL + "/embedding"; + } + + client.addString(jsonRequest); + client.execute("POST", providerURL); + if (client.getStatusCode() == 200) { + if (client.getHeader("Content-Type").contains("text/event-stream")){ + return null; + } + else { + String saiaResponse = client.getString(); + logger.debug("Agent response: " + saiaResponse); + JSONObject jsonResponse = new JSONObject(saiaResponse); + return new ObjectMapper().readValue(jsonResponse.toString(), OpenAIResponse.class); + } + } + else { + String errorDescription = String.format("Error calling Enterprise AI API, StatusCode: %d, ReasonLine: %s", + client.getStatusCode(), + client.getReasonLine()); + addResultMessage("SAIA_ERROR_CALL", (byte)1, errorDescription, result); + logger.error(errorDescription); + logger.debug("Agent error response: " + client.getString()); + } + } + catch (Exception e) { + addResultMessage("SAIA_EXCEPTION", (byte)1, e.getMessage(), result); + logger.error("Calling Enterprise AI API Error", e); + } + return null; + } + + + private static void addResultMessage(String id, byte type, String description, CallResult result){ + if (type == 1) + result.setFail(); + SdtMessages_Message msg = new SdtMessages_Message(); + msg.setgxTv_SdtMessages_Message_Id(id); + msg.setgxTv_SdtMessages_Message_Type(type); + msg.setgxTv_SdtMessages_Message_Description(description); + result.addMessage(msg); + } +} diff --git a/java/src/test/java/com/genexus/agent/Agent.java b/java/src/test/java/com/genexus/agent/Agent.java new file mode 100644 index 000000000..2098be5fc --- /dev/null +++ b/java/src/test/java/com/genexus/agent/Agent.java @@ -0,0 +1,154 @@ +package com.genexus.agent; + +import com.genexus.*; +import com.genexus.util.CallResult; +import com.genexus.util.ChatResult; +import com.genexus.util.saia.OpenAIResponse; +import java.util.ArrayList; + +public final class Agent extends GXProcedure +{ + public Agent(int remoteHandle ) + { + super( remoteHandle , new ModelContext( Agent.class ), "" ); + } + + public void execute( String aP0 , + String aP1 , + String[] aP2 ) + { + execute_int(aP0, aP1, aP2); + } + + private void execute_int( String aP0 , + String aP1 , + String[] aP2 ) + { + AV3Parameter1 = aP0; + AV4Parameter2 = aP1; + this.aP2 = aP2; + privateExecute(); + } + + protected void privateExecute( ) + { + Gxproperties = new com.genexus.util.GXProperties(); + ArrayList messages = new ArrayList();; + if (AV3Parameter1.equals("chat")) { + OpenAIResponse.Message message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Dime el clima en Lima - Peru"); + messages.add(message); + message = new OpenAIResponse.Message(); + message.setRole("assistant"); + message.setStringContent("El clima actual en Lima, Perú, es soleado con una temperatura de 20.9°C (69.6°F). La dirección del viento es del suroeste (SSW) a 15.1 km/h (9.4 mph), y la humedad relativa es del 68%. La presión atmosférica es de 1013 mb. La visibilidad es de 10 km y el índice UV es de 12.5."); + messages.add(message); + message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Que me puedes contar de la ciudad que te pedi el clima previamente?"); + messages.add(message); + AV5OutputVariable = callAgent( "The weatherman", Gxproperties, messages, new CallResult()) ; + } + else if (AV3Parameter1.equals("eval_image")) { + OpenAIResponse.StructuredContent content = new OpenAIResponse.StructuredContent(); + ArrayList items = new ArrayList<>(); + OpenAIResponse.StructuredContentItem contentItem = new OpenAIResponse.StructuredContentItem(); + contentItem.setType("text"); + contentItem.setText("De que se trata esta imagen"); + items.add(contentItem); + OpenAIResponse.StructuredContentItem contentItem1 = new OpenAIResponse.StructuredContentItem(); + contentItem1.setType("image_url"); + OpenAIResponse.StructuredContentItem.ImageUrl imageURL = new OpenAIResponse.StructuredContentItem.ImageUrl(); + imageURL.setUrl("https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"); + contentItem1.setImage_url(imageURL); + items.add(contentItem1); + content.setItems(items); + OpenAIResponse.Message message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStructuredContent(content); + messages.add(message); + AV5OutputVariable = callAgent( "The weatherman", Gxproperties, messages, new CallResult()) ; + } + else if (AV3Parameter1.equals("chat_stream")) { + OpenAIResponse.Message message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Dime el clima en Lima - Peru"); + messages.add(message); + message = new OpenAIResponse.Message(); + message.setRole("assistant"); + message.setStringContent("El clima actual en Lima, Perú, es soleado con una temperatura de 20.9°C (69.6°F). La dirección del viento es del suroeste (SSW) a 15.1 km/h (9.4 mph), y la humedad relativa es del 68%. La presión atmosférica es de 1013 mb. La visibilidad es de 10 km y el índice UV es de 12.5."); + messages.add(message); + message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Que me puedes contar de la ciudad que te pedi el clima previamente?"); + messages.add(message); + ChatResult chatResult = chatAgent( "The weatherman", Gxproperties, messages, new CallResult()) ; + while (chatResult.hasMoreData()) { + System.out.print(chatResult.hasMoreData()); + } + } + else if (AV3Parameter1.equals("toolcall")) { + OpenAIResponse.Message message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Necesito nombre y descripcion del producto 1779"); + messages.add(message); + AV5OutputVariable = callAgent( "ProductInfo", Gxproperties, messages, new CallResult()) ; + message = new OpenAIResponse.Message(); + message.setRole("assistant"); + message.setStringContent(AV5OutputVariable); + messages.add(message); + message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Quiero que traduzcas la descripcion del producto que me habias enviado previamente"); + messages.add(message); + AV5OutputVariable = callAgent( "ProductInfo", Gxproperties, messages, new CallResult()) ; + } + else if (AV3Parameter1.equals("toolcall_stream")) { + OpenAIResponse.Message message = new OpenAIResponse.Message(); + message.setRole("user"); + message.setStringContent("Necesito nombre y descripcion del producto 1779"); + messages.add(message); + ChatResult chatResult = chatAgent( "ProductInfo", Gxproperties, messages, new CallResult()) ; + while (chatResult.hasMoreData()) { + System.out.print(chatResult.hasMoreData()); + } + } + else { + Gxproperties.set("&Parameter1", AV3Parameter1); + Gxproperties.set("&Parameter2", AV4Parameter2); + Gxproperties.set("$context", "Los Angeles"); + AV5OutputVariable = callAgent( "The weatherman", Gxproperties, messages, new CallResult()) ; + } + cleanup(); + } + + protected void cleanup( ) + { + this.aP2[0] = AV5OutputVariable; + } + + protected String callTool(String name, String arguments) { + switch (name) { + case "TranslateDescription": + return "The Panavox Television 80 inches are wonderful"; + case "GetProductName": + return "Televisor Panavox 80 pulgadas"; + case "GetProductDescription": + return "Flor de Televisor el Panavox de 80 pulgadas"; + default: + return String.format("Unknown function %s", name); + } + } + + public void initialize( ) + { + } + + String AV3Parameter1 ; + String AV4Parameter2 ; + String AV5OutputVariable ; + com.genexus.util.GXProperties Gxproperties ; + String[] aP2 ; +} + + diff --git a/java/src/test/java/com/genexus/agent/TestAgent.java b/java/src/test/java/com/genexus/agent/TestAgent.java new file mode 100644 index 000000000..043c61b1e --- /dev/null +++ b/java/src/test/java/com/genexus/agent/TestAgent.java @@ -0,0 +1,44 @@ +package com.genexus.agent; + +import com.genexus.Application; +import com.genexus.sampleapp.GXcfg; +import com.genexus.specific.java.Connect; +import com.genexus.specific.java.LogManager; +import org.junit.Before; +import org.junit.Test; + +public class TestAgent { + + @Before + public void setUpStreams() { + Connect.init(); + LogManager.initialize("."); + Application.init(GXcfg.class); + } + + @Test + public void testAPICallAgent() { + String[] GXv_char4 = new String[1] ; + new Agent(-1).execute( "Today", "Tomorrow", GXv_char4) ; + System.out.println(GXv_char4[0]); + + String[] GXv_char5 = new String[1] ; + new Agent(-1).execute( "chat", "", GXv_char5) ; + System.out.println(GXv_char5[0]); + + String[] GXv_char9 = new String[1] ; + new Agent(-1).execute( "eval_image", "", GXv_char9) ; + System.out.println(GXv_char9[0]); + + String[] GXv_char8 = new String[1] ; + new Agent(-1).execute( "chat_stream", "", GXv_char8) ; + + String[] GXv_char6 = new String[1] ; + new Agent(-1).execute( "toolcall", "", GXv_char6) ; + System.out.println(); + System.out.println(GXv_char6[0]); + + String[] GXv_char7 = new String[1] ; + new Agent(-1).execute( "toolcall_stream", "", GXv_char7) ; + } +} diff --git a/java/src/test/java/com/genexus/embedding/GXEmbeddingTest.java b/java/src/test/java/com/genexus/embedding/GXEmbeddingTest.java new file mode 100644 index 000000000..d4af124e3 --- /dev/null +++ b/java/src/test/java/com/genexus/embedding/GXEmbeddingTest.java @@ -0,0 +1,35 @@ +package com.genexus.embedding; + +import com.genexus.Application; +import com.genexus.GXBaseCollection; +import com.genexus.SdtMessages_Message; +import com.genexus.db.GXEmbedding; +import com.genexus.sampleapp.GXcfg; +import com.genexus.specific.java.Connect; +import com.genexus.specific.java.LogManager; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; +import java.util.stream.Collectors; + +public class GXEmbeddingTest { + @Test + public void EmbeddingTest(){ + Connect.init(); + LogManager.initialize("."); + Application.init(GXcfg.class); + List embedding = GXEmbedding.getEmbedding("openai/text-embedding-3-small", 512, "Hello World"); + String result = embedding.stream() + .map(String::valueOf) + .collect(Collectors.joining(",")); + System.out.println("Embedding: " + result); + Assert.assertNotNull(embedding); + + GXBaseCollection AV8Messages = new GXBaseCollection<>(); + GXEmbedding A7ProductEmbedding = new GXEmbedding("openai/text-embedding-3-small",512) ; + GXEmbedding AV9ProductEmbedding = GXEmbedding.generateEmbedding(A7ProductEmbedding, "", AV8Messages) ; + result = AV9ProductEmbedding.toString(); + System.out.println("Empty Embedding: " + result); + } +}