values
540 | ) {
541 | }
542 |
543 | /**
544 | * Information on a model
545 | *
546 | * @param name The resource name of the Model.
547 | *
548 | * Format: models/{model} with a {model} naming convention of:
549 | *
550 | * "{baseModelId}-{version}"
551 | * Examples:
552 | *
553 | * models/chat-bison-001
554 | * @param baseModelId The name of the base model, pass this to the generation request.
555 | * @param version The version number of the model.
556 | * @param displayName The human-readable name of the model. E.g. "Chat Bison".
557 | *
558 | * The name can be up to 128 characters long and can consist of any UTF-8 characters.
559 | * @param description A short description of the model.
560 | * @param inputTokenLimit Maximum number of input tokens allowed for this model.
561 | * @param outputTokenLimit Maximum number of output tokens available for this model.
562 | * @param supportedGenerationMethods The model's supported generation methods.
563 | *
564 | * The method names are defined as Pascal case strings, such as generateMessage which correspond to API methods.
565 | * @param temperature Controls the randomness of the output.
566 | *
567 | * Values can range over [0.0,2.0], inclusive.
568 | * A higher value will produce responses that are more varied,
569 | * while a value closer to 0.0 will typically result in less surprising responses from the model.
570 | * This value specifies default to be used by the backend while making the call to the model.
571 | * @param topP For Nucleus sampling.
572 | *
573 | * Nucleus sampling considers the smallest set of tokens whose probability sum is at least topP.
574 | * This value specifies default to be used by the backend while making the call to the model.
575 | * @param topK For Top-k sampling.
576 | *
577 | * Top-k sampling considers the set of topK most probable tokens.
578 | * This value specifies default to be used by the backend while making the call to the model.
579 | * If empty, indicates the model doesn't use top-k sampling, and topK isn't allowed as a generation parameter.
580 | */
581 | public record Model(
582 | String name,
583 | String baseModelId,
584 | String version,
585 | String displayName,
586 | String description,
587 | int inputTokenLimit,
588 | int outputTokenLimit,
589 | List supportedGenerationMethods,
590 | double temperature,
591 | double topP,
592 | int topK
593 | ) {
594 | }
595 |
596 | private GeneratedContent parse(String body, UUID uuid) {
597 | try {
598 | var gcr = jsonParser.fromJson(body, GenerateContentResponse.class);
599 | // each element can just replace the previous one
600 | this.responseById.put(uuid, gcr);
601 | // we assume we always get a candidate. Otherwise, there is probably something wrong with the input
602 | var candidate = gcr.candidates().get(0);
603 | if (candidate.content() == null) {
604 | return new GeneratedContent(uuid, "", null, candidate.finishReason());
605 | }
606 | GenerationPart firstPart = candidate.content().parts().get(0);
607 | return new GeneratedContent(uuid, firstPart.text(), firstPart.functionCall(), candidate.finishReason());
608 | } catch (Exception e) {
609 | throw new GeminiException("Unexpected body:\n" + body, e);
610 | }
611 | }
612 |
613 | private record BatchEmbedContentRequest(
614 | List requests
615 | ) {
616 | }
617 |
618 | private record EmbedContentRequest(
619 | String model,
620 | GenerationContent content,
621 | String taskType,
622 | String title,
623 | Long outputDimensionality
624 | ) {
625 | }
626 |
627 | private record BatchEmbedContentResponse(
628 | List embeddings
629 | ) {
630 | }
631 |
632 | private record CountTokenRequest(
633 | GenerateContentRequest generateContentRequest
634 | ) {
635 | }
636 |
637 | private record CountTokenResponse(
638 | Long totalTokens
639 | ) {
640 | }
641 |
642 | private record GenerateContentResponse(
643 | UsageMetadata usageMetadata,
644 | List candidates
645 | ) {
646 | }
647 |
648 | private record ResponseCandidate(
649 | GenerationContent content,
650 | String finishReason,
651 | int index,
652 | List safetyRatings
653 | ) {
654 | }
655 |
656 | private record GenerateContentRequest(
657 | // for some reason, model is required for countToken, but not for the others.
658 | // But it seems to be acceptable for the others, so we just add it to all for now
659 | String model,
660 | List contents,
661 | List safetySettings,
662 | GenerationConfig generationConfig,
663 | SystemInstruction systemInstruction,
664 | List tools
665 | ) {
666 | }
667 |
668 | /**
669 | * See Tool
670 | */
671 | private record Tool(
672 | List functionDeclarations
673 | // still missing CodeExecution and GoogleSearchRetrieval
674 | ) {
675 | }
676 |
677 | private record SystemInstruction(
678 | List parts
679 | ) {
680 | }
681 |
682 | private record SystemInstructionPart(
683 | String text
684 | ) {
685 | }
686 |
687 | private record GenerationContent(
688 | String role,
689 | List parts
690 | ) {
691 | }
692 |
693 | /**
694 | * See Part
695 | */
696 | private record GenerationPart(
697 | // contains one of these
698 | String text,
699 | InlineData inline_data,
700 | FunctionCall functionCall,
701 | FunctionResponse functionResponse
702 | ) {
703 | }
704 |
705 | private record InlineData(
706 | String mime_type,
707 | String data
708 | ) {
709 | }
710 |
711 | private record ModelResponse(List models) {
712 | }
713 |
714 | private interface ThrowingSupplier {
715 | T get() throws IOException, InterruptedException;
716 | }
717 | }
718 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/api/GenerationConfig.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.api;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Generation configuration.
8 | *
9 | * @param stopSequences Optional. The set of character sequences (up to 5) that will stop output generation.
10 | * If specified, the API will stop at the first appearance of a stop sequence.
11 | * The stop sequence will not be included as part of the response.
12 | * @param responseMimeType Optional. Output response mimetype of the generated candidate text.
13 | * Supported mimetype: text/plain: (default) Text output.
14 | * application/json: JSON response in the candidates.
15 | * @param responseSchema Optional. Output response schema of the generated candidate text when response mime type can have schema.
16 | * Schema can be objects, primitives or arrays and is a subset of OpenAPI schema.
17 | * If set, a compatible responseMimeType must also be set. Compatible mimetypes: application/json: Schema for JSON response.
18 | * @param maxOutputTokens Optional. The maximum number of tokens to include in a candidate.
19 | * Note: The default value varies by model, see the Model.output_token_limit attribute of the Model returned from the getModel function.
20 | * @param temperature Optional. Controls the randomness of the output.
21 | * Note: The default value varies by model, see the Model. temperature attribute of the Model returned from the getModel function.
22 | * Values can range from [0.0, 2.0].
23 | * @param topP Optional. The maximum cumulative probability of tokens to consider when sampling.
24 | * The model uses combined Top-k and nucleus sampling.
25 | * Tokens are sorted based on their assigned probabilities so that only the most likely tokens are considered.
26 | * Top-k sampling directly limits the maximum number of tokens to consider, while Nucleus sampling limits number of tokens based on the cumulative probability.
27 | * Note: The default value varies by model, see the Model.top_p attribute of the Model returned from the getModel function.
28 | * @param topK Optional. The maximum number of tokens to consider when sampling.
29 | * Models use nucleus sampling or combined Top-k and nucleus sampling.
30 | * Top-k sampling considers the set of topK most probable tokens.
31 | * Models running with nucleus sampling don't allow topK setting.
32 | * Note: The default value varies by model, see the Model.top_k attribute of the Model returned from the getModel function.
33 | * Empty topK field in Model indicates the model doesn't apply top-k sampling and doesn't allow setting topK on requests.
34 | * @see GenerationConfig
35 | */
36 | public record GenerationConfig(
37 | List stopSequences,
38 | String responseMimeType,
39 | Schema responseSchema,
40 | Integer maxOutputTokens,
41 | Double temperature,
42 | Double topP,
43 | Integer topK
44 | ) {
45 |
46 | /**
47 | * Builder for {@link GenerationConfig}.
48 | */
49 | public static GenerationConfigBuilder builder() {
50 | return new GenerationConfigBuilder();
51 | }
52 |
53 | public static class GenerationConfigBuilder {
54 | private final List stopSequences = new ArrayList<>();
55 | private String responseMimeType;
56 | private Schema responseSchema;
57 | private Integer maxOutputTokens;
58 | private Double temperature;
59 | private Double topP;
60 | private Integer topK;
61 |
62 | public GenerationConfigBuilder addStopSequence(String stopSequence) {
63 | this.stopSequences.add(stopSequence);
64 | return this;
65 | }
66 |
67 | public GenerationConfigBuilder responseMimeType(String responseMimeType) {
68 | this.responseMimeType = responseMimeType;
69 | return this;
70 | }
71 |
72 | public GenerationConfigBuilder responseSchema(Schema responseSchema) {
73 | this.responseSchema = responseSchema;
74 | return this;
75 | }
76 |
77 | public GenerationConfigBuilder maxOutputTokens(Integer maxOutputTokens) {
78 | this.maxOutputTokens = maxOutputTokens;
79 | return this;
80 | }
81 |
82 | public GenerationConfigBuilder temperature(Double temperature) {
83 | this.temperature = temperature;
84 | return this;
85 | }
86 |
87 | public GenerationConfigBuilder topP(Double topP) {
88 | this.topP = topP;
89 | return this;
90 | }
91 |
92 | public GenerationConfigBuilder topK(Integer topK) {
93 | this.topK = topK;
94 | return this;
95 | }
96 |
97 | public GenerationConfig build() {
98 | return new GenerationConfig(
99 | stopSequences.isEmpty() ? null : stopSequences,
100 | responseMimeType,
101 | responseSchema,
102 | maxOutputTokens,
103 | temperature,
104 | topP,
105 | topK
106 | );
107 | }
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/api/GenerativeModel.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.api;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Contains all the information needed for Gemini API to generate new content.
8 | *
9 | * @param modelName to be used. see {@link ModelVariant}. Must start with "models/"
10 | * @param contents given as input to Gemini API
11 | * @param safetySettings optional, to adjust safety settings
12 | * @param generationConfig optional, to configure the prompt
13 | * @param systemInstruction optional, system instruction
14 | * @param functionDeclarations optional, functions the model may call
15 | */
16 | public record GenerativeModel(
17 | String modelName,
18 | List contents,
19 | List safetySettings,
20 | GenerationConfig generationConfig,
21 | List systemInstruction,
22 | List functionDeclarations
23 | ) {
24 |
25 | /**
26 | * Create a {@link GenerativeModelBuilder}.
27 | *
28 | * @return an empty {@link GenerativeModelBuilder}
29 | */
30 | public static GenerativeModelBuilder builder() {
31 | return new GenerativeModelBuilder();
32 | }
33 |
34 | /**
35 | * A builder for {@link GenerativeModel}. Currently, does not validate the fields when building the model. Not thread-safe.
36 | */
37 | public static class GenerativeModelBuilder {
38 | private String modelName;
39 | private GenerationConfig generationConfig;
40 | private final List contents = new ArrayList<>();
41 | private final List safetySettings = new ArrayList<>();
42 | private final List systemInstructions = new ArrayList<>();
43 | private final List functionDeclarations = new ArrayList<>();
44 |
45 | private GenerativeModelBuilder() {
46 | }
47 |
48 | /**
49 | * Set the model name.
50 | *
51 | * @param modelName to be set
52 | * @return this
53 | */
54 | public GenerativeModelBuilder modelName(String modelName) {
55 | this.modelName = modelName;
56 | return this;
57 | }
58 |
59 | /**
60 | * Set the model name.
61 | *
62 | * @param modelVariant to be set
63 | * @return this
64 | */
65 | public GenerativeModelBuilder modelName(ModelVariant modelVariant) {
66 | return modelName(modelVariant == null ? null : modelVariant.variant());
67 | }
68 |
69 | /**
70 | * Add content
71 | *
72 | * @param content to be added
73 | * @return this
74 | */
75 | public GenerativeModelBuilder addContent(Content content) {
76 | this.contents.add(content);
77 | return this;
78 | }
79 |
80 | /**
81 | * Add system instruction
82 | *
83 | * @param systemInstruction to be added
84 | * @return this
85 | */
86 | public GenerativeModelBuilder addSystemInstruction(String systemInstruction) {
87 | this.systemInstructions.add(systemInstruction);
88 | return this;
89 | }
90 |
91 | /**
92 | * Add safety setting
93 | *
94 | * @param safetySetting to be added
95 | * @return this
96 | */
97 | public GenerativeModelBuilder addSafetySetting(SafetySetting safetySetting) {
98 | this.safetySettings.add(safetySetting);
99 | return this;
100 | }
101 |
102 | /**
103 | * Add function declarations
104 | *
105 | * @param functionDeclaration to be added
106 | * @return this
107 | */
108 | public GenerativeModelBuilder addFunctionDeclaration(FunctionDeclaration functionDeclaration) {
109 | this.functionDeclarations.add(functionDeclaration);
110 | return this;
111 | }
112 |
113 | /**
114 | * Set the generation config
115 | *
116 | * @param generationConfig to be set
117 | * @return this
118 | */
119 | public GenerativeModelBuilder generationConfig(GenerationConfig generationConfig) {
120 | this.generationConfig = generationConfig;
121 | return this;
122 | }
123 |
124 | /**
125 | * Build the model based on this builder.
126 | *
127 | * @return a completed (not necessarily validated) {@link GenerativeModel}
128 | */
129 | public GenerativeModel build() {
130 | return new GenerativeModel(
131 | modelName,
132 | contents,
133 | safetySettings,
134 | generationConfig,
135 | systemInstructions,
136 | functionDeclarations
137 | );
138 | }
139 | }
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/api/ModelVariant.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.api;
2 |
3 | /**
4 | * (Potentially non-exhaustive) list of supported models.
5 | *
6 | * @see Gemini Models
7 | */
8 | public enum ModelVariant {
9 | /**
10 | * Next generation features, speed, and multimodal generation for a diverse variety of tasks.
11 | *
12 | * - Input: Audio, images, videos, and text
13 | * - Output: Text, images (coming soon), and audio (coming soon)
14 | *
15 | */
16 | GEMINI_2_0_FLASH_EXP("gemini-2.0-flash-exp"),
17 | /**
18 | * Complex reasoning tasks such as code and text generation, text editing, problem-solving, data extraction and generation.
19 | */
20 | GEMINI_1_5_PRO("gemini-1.5-pro"),
21 | /**
22 | * Fast and versatile performance across a diverse variety of tasks.
23 | */
24 | GEMINI_1_5_FLASH("gemini-1.5-flash"),
25 | /**
26 | * High volume and lower intelligence tasks.
27 | */
28 | GEMINI_1_5_FLASH_8B("gemini-1.5-flash-8b"),
29 | /**
30 | * Natural language tasks, multi-turn text and code chat, and code generation.
31 | *
32 | * @deprecated on 2/15/2025
33 | */
34 | @Deprecated
35 | GEMINI_1_0_PRO("gemini-1.0-pro"),
36 | /**
37 | * Measuring the relatedness of text strings.
38 | */
39 | TEXT_EMBEDDING_004("text-embedding-004"),
40 | /**
41 | * Providing source-grounded answers to questions.
42 | */
43 | AQA("aqa"),
44 | ;
45 |
46 | private final String variant;
47 |
48 | ModelVariant(String variant) {
49 | this.variant = variant;
50 | }
51 |
52 | /**
53 | * Model variant name.
54 | *
55 | * @return Model variant name as needed by Gemini API
56 | */
57 | public String variant() {
58 | return "models/" + variant;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/api/SafetySetting.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.api;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | import static swiss.ameri.gemini.api.SafetySetting.HarmCategoryType.*;
7 |
8 | /**
9 | * Safety settings according to SafetySetting.
10 | *
11 | * @param category the harm category, see {@link HarmCategory}
12 | * @param threshold the threshold, see {@link HarmBlockThreshold}
13 | */
14 | public record SafetySetting(
15 | String category,
16 | String threshold
17 | ) {
18 |
19 | /**
20 | * Create a SafetySetting by using the provided enums. Use the constructor for custom string values that might
21 | * be missing in the enums.
22 | *
23 | * @param category the harm category, see {@link HarmCategory}
24 | * @param threshold the threshold, see {@link HarmBlockThreshold}
25 | * @return the new {@link SafetySetting}
26 | */
27 | public static SafetySetting of(
28 | HarmCategory category,
29 | HarmBlockThreshold threshold
30 | ) {
31 | return new SafetySetting(
32 | category == null ? null : category.name(),
33 | threshold == null ? null : threshold.name()
34 | );
35 | }
36 |
37 | public enum HarmCategoryType {
38 | GEMINI,
39 | PALM,
40 | UNKNOWN
41 | }
42 |
43 | /**
44 | * According to HarmCategory.
45 | * See {@link #harmCategoryType} for which can be used as input to a model.
46 | */
47 | public enum HarmCategory {
48 |
49 | /**
50 | * Harasment content.
51 | */
52 | HARM_CATEGORY_HARASSMENT(GEMINI),
53 | /**
54 | * Hate speech and content.
55 | */
56 | HARM_CATEGORY_HATE_SPEECH(GEMINI),
57 | /**
58 | * Sexually explicit content.
59 | */
60 | HARM_CATEGORY_SEXUALLY_EXPLICIT(GEMINI),
61 | /**
62 | * Dangerous content.
63 | */
64 | HARM_CATEGORY_DANGEROUS_CONTENT(GEMINI),
65 | /**
66 | * Content that may be used to harm civic integrity.
67 | */
68 | HARM_CATEGORY_CIVIC_INTEGRITY(GEMINI),
69 | /**
70 | * Category is unspecified.
71 | */
72 | HARM_CATEGORY_UNSPECIFIED(UNKNOWN),
73 | /**
74 | * Negative or harmful comments targeting identity and/or protected attribute.
75 | */
76 | HARM_CATEGORY_DEROGATORY(PALM),
77 | /**
78 | * Content that is rude, disrespectful, or profane.
79 | */
80 | HARM_CATEGORY_TOXICITY(PALM),
81 | /**
82 | * Describes scenarios depicting violence against an individual or group, or general descriptions of gore.
83 | */
84 | HARM_CATEGORY_VIOLENCE(PALM),
85 | /**
86 | * Contains references to sexual acts or other lewd content.
87 | */
88 | HARM_CATEGORY_SEXUAL(PALM),
89 | /**
90 | * Promotes unchecked medical advice.
91 | */
92 | HARM_CATEGORY_MEDICAL(PALM),
93 | /**
94 | * Dangerous content that promotes, facilitates, or encourages harmful acts.
95 | */
96 | HARM_CATEGORY_DANGEROUS(PALM);
97 | private final HarmCategoryType harmCategoryType;
98 |
99 | HarmCategory(HarmCategoryType harmCategoryType) {
100 | this.harmCategoryType = harmCategoryType;
101 | }
102 |
103 | public HarmCategoryType harmCategoryType() {
104 | return harmCategoryType;
105 | }
106 |
107 | public static List harmCategoriesFor(HarmCategoryType type) {
108 | return Arrays.stream(values())
109 | .filter(category -> category.harmCategoryType == type)
110 | .toList();
111 | }
112 | }
113 |
114 | /**
115 | * According to SafetySetting
116 | */
117 | public enum HarmBlockThreshold {
118 | /**
119 | * Threshold is unspecified.
120 | */
121 | HARM_BLOCK_THRESHOLD_UNSPECIFIED,
122 | /**
123 | * Content with NEGLIGIBLE will be allowed.
124 | */
125 | BLOCK_LOW_AND_ABOVE,
126 | /**
127 | * Content with NEGLIGIBLE and LOW will be allowed.
128 | */
129 | BLOCK_MEDIUM_AND_ABOVE,
130 | /**
131 | * Content with NEGLIGIBLE, LOW, and MEDIUM will be allowed.
132 | */
133 | BLOCK_ONLY_HIGH,
134 | /**
135 | * All content will be allowed.
136 | */
137 | BLOCK_NONE
138 | }
139 |
140 | /**
141 | * The probability that a piece of content is harmful.
142 | * The classification system gives the probability of the content being unsafe.
143 | * This does not indicate the severity of harm for a piece of content.
144 | */
145 | public enum HarmProbability {
146 |
147 | /**
148 | * Probability is unspecified.
149 | */
150 | HARM_PROBABILITY_UNSPECIFIED,
151 |
152 | /**
153 | * Content has a negligible chance of being unsafe.
154 | */
155 | NEGLIGIBLE,
156 |
157 | /**
158 | * Content has a low chance of being unsafe.
159 | */
160 | LOW,
161 |
162 | /**
163 | * Content has a medium chance of being unsafe.
164 | */
165 | MEDIUM,
166 |
167 | /**
168 | * Content has a high chance of being unsafe.
169 | */
170 | HIGH
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/api/Schema.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.api;
2 |
3 |
4 | import java.util.List;
5 | import java.util.Map;
6 |
7 | /**
8 | * The Schema object allows the definition of input and output data types.
9 | * These types can be objects, but also primitives and arrays.
10 | * Represents a select subset of an OpenAPI 3.0 schema object.
11 | *
12 | * @param type Required. Data type.
13 | * @param format Optional. The format of the data. This is used only for primitive datatypes.
14 | * Supported formats:
15 | * for NUMBER type: float, double
16 | * for INTEGER type: int32, int64
17 | * for STRING type: enum
18 | * @param description Optional. A brief description of the parameter. This could contain examples of use.
19 | * Parameter description may be formatted as Markdown.
20 | * @param nullable Optional. Indicates if the value may be null.
21 | * @param ameri_swiss_enum Optional. Note: the ameri_swiss prefix must be removed by the {@link swiss.ameri.gemini.spi.JsonParser}.
22 | * Possible values of the element of Type.STRING with enum format.
23 | * For example we can define an Enum Direction as :
24 | * {type:STRING, format:enum, enum:["EAST", NORTH", "SOUTH", "WEST"]}
25 | * @param maxItems Optional. Maximum number of the elements for Type.ARRAY.
26 | * @param minItems Optional. Minimum number of the elements for Type.ARRAY.
27 | * @param properties Optional. Properties of Type.OBJECT.
28 | * An object containing a list of "key": value pairs. Example:
29 | * { "name": "wrench", "mass": "1.3kg", "count": "3" }
.
30 | * @param required Optional. Required properties of Type.OBJECT.
31 | * @param items Optional. Schema of the elements of Type.ARRAY.
32 | * @see Schema for further information.
33 | */
34 | public record Schema(
35 | Type type,
36 | String format,
37 | String description,
38 | Boolean nullable,
39 | List ameri_swiss_enum,
40 | String maxItems,
41 | String minItems,
42 | Map properties,
43 | List required,
44 | Schema items
45 | ) {
46 |
47 |
48 | /**
49 | * Create a {@link SchemaBuilder}.
50 | *
51 | * @return an empty {@link SchemaBuilder}
52 | */
53 | public static SchemaBuilder builder() {
54 | return new SchemaBuilder();
55 | }
56 |
57 | /**
58 | * A builder for {@link Schema}. Currently, does not validate the fields when building the model. Not thread-safe.
59 | */
60 | public static class SchemaBuilder {
61 | private Type type;
62 | private String format;
63 | private String description;
64 | private Boolean nullable;
65 | private List ameri_swiss_enum;
66 | private String maxItems;
67 | private String minItems;
68 | private Map properties;
69 | private List required;
70 | private Schema items;
71 |
72 |
73 | private SchemaBuilder() {
74 | }
75 |
76 | public Schema build() {
77 | return new Schema(
78 | this.type,
79 | this.format,
80 | this.description,
81 | this.nullable,
82 | this.ameri_swiss_enum,
83 | this.maxItems,
84 | this.minItems,
85 | this.properties,
86 | this.required,
87 | this.items
88 | );
89 | }
90 |
91 | public SchemaBuilder type(Type type) {
92 | this.type = type;
93 | return this;
94 | }
95 |
96 | public SchemaBuilder format(String format) {
97 | this.format = format;
98 | return this;
99 | }
100 |
101 | public SchemaBuilder description(String description) {
102 | this.description = description;
103 | return this;
104 | }
105 |
106 | public SchemaBuilder nullable(Boolean nullable) {
107 | this.nullable = nullable;
108 | return this;
109 | }
110 |
111 | public SchemaBuilder ameri_swiss_enum(List ameri_swiss_enum) {
112 | this.ameri_swiss_enum = ameri_swiss_enum;
113 | return this;
114 | }
115 |
116 | public SchemaBuilder maxItems(String maxItems) {
117 | this.maxItems = maxItems;
118 | return this;
119 | }
120 |
121 | public SchemaBuilder minItems(String minItems) {
122 | this.minItems = minItems;
123 | return this;
124 | }
125 |
126 | public SchemaBuilder properties(Map properties) {
127 | this.properties = properties;
128 | return this;
129 | }
130 |
131 | public SchemaBuilder required(List required) {
132 | this.required = required;
133 | return this;
134 | }
135 |
136 | public SchemaBuilder items(Schema items) {
137 | this.items = items;
138 | return this;
139 | }
140 | }
141 |
142 | /**
143 | * Type contains the list of OpenAPI data types.
144 | *
145 | * @see Data types
146 | */
147 | public enum Type {
148 | /**
149 | * Not specified, should not be used.
150 | */
151 | TYPE_UNSPECIFIED,
152 | /**
153 | * String type.
154 | */
155 | STRING,
156 | /**
157 | * Number type.
158 | */
159 | NUMBER,
160 | /**
161 | * Integer type.
162 | */
163 | INTEGER,
164 | /**
165 | * Boolean type.
166 | */
167 | BOOLEAN,
168 | /**
169 | * Array type.
170 | */
171 | ARRAY,
172 | /**
173 | * Object type.
174 | */
175 | OBJECT
176 | }
177 | }
178 |
179 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/api/TaskType.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.api;
2 |
3 | /**
4 | * Type of task for which the embedding will be used.
5 | */
6 | public enum TaskType {
7 | /**
8 | * Unset value, which will default to one of the other enum values.
9 | */
10 | TASK_TYPE_UNSPECIFIED,
11 |
12 | /**
13 | * Specifies the given text is a query in a search/retrieval setting.
14 | */
15 | RETRIEVAL_QUERY,
16 |
17 | /**
18 | * Specifies the given text is a document from the corpus being searched.
19 | */
20 | RETRIEVAL_DOCUMENT,
21 |
22 | /**
23 | * Specifies the given text will be used for Semantic Textual Similarity (STS).
24 | */
25 | SEMANTIC_SIMILARITY,
26 |
27 | /**
28 | * Specifies that the given text will be classified.
29 | */
30 | CLASSIFICATION,
31 |
32 | /**
33 | * Specifies that the embeddings will be used for clustering.
34 | */
35 | CLUSTERING,
36 |
37 | /**
38 | * Specifies that the given text will be used for question answering.
39 | */
40 | QUESTION_ANSWERING,
41 |
42 | /**
43 | * Specifies that the given text will be used for fact verification.
44 | */
45 | FACT_VERIFICATION
46 | }
47 |
--------------------------------------------------------------------------------
/gemini-api/src/main/java/swiss/ameri/gemini/spi/JsonParser.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.spi;
2 |
3 | /**
4 | * Used to (un-) marshal java objects (mainly {@code record}s) to JSON Strings.
5 | * To keep this library dependency free, no implementation is provided directly.
6 | * {@code swiss.ameri:gemini-gson} provides an example implementation using a gson dependency.
7 | */
8 | public interface JsonParser {
9 |
10 | /**
11 | * This method serializes the specified object into its equivalent JSON representation.
12 | *
13 | * @param object to be serialized
14 | * @return the serialized object
15 | */
16 | String toJson(Object object);
17 |
18 | /**
19 | * This method deserializes the specified JSON into an object of the specified class.
20 | *
21 | * @param json to be deserialized
22 | * @param clazz to be created from the json
23 | * @param type of the class
24 | * @return the deserialized object
25 | */
26 | T fromJson(String json, Class clazz);
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/gemini-gson/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | swiss.ameri
9 | gemini
10 | 1beta.0.2.8-SNAPSHOT
11 |
12 |
13 | gemini-gson
14 |
15 | Provides an example GSON implementation for JSON operations.
16 |
17 |
18 |
19 | 17
20 |
21 | ${project.version}
22 | 2.11.0
23 | swiss.ameri.gemini.gson
24 |
25 |
26 |
27 |
28 |
29 | swiss.ameri
30 | gemini-api
31 | ${gemini.version}
32 |
33 |
34 |
35 | com.google.code.gson
36 | gson
37 | ${gson.version}
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/gemini-gson/src/main/java/swiss/ameri/gemini/gson/GsonJsonParser.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.gson;
2 |
3 | import com.google.gson.FieldNamingStrategy;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 | import swiss.ameri.gemini.api.Schema;
7 | import swiss.ameri.gemini.spi.JsonParser;
8 |
9 | /**
10 | * Reference implementation of {@link JsonParser} using {@link Gson} dependency.
11 | */
12 | public class GsonJsonParser implements JsonParser {
13 |
14 | /**
15 | * Field naming strategy to avoid usage of illegal field names in java.
16 | * See e.g. {@link Schema#ameri_swiss_enum()}, which cannot be named {@code enum}.
17 | */
18 | public static final FieldNamingStrategy FIELD_NAMING_STRATEGY = field -> {
19 | if (field.getName().startsWith("ameri_swiss_")) {
20 | return field.getName().substring("ameri_swiss_".length());
21 | }
22 | return field.getName();
23 | };
24 |
25 | private final Gson gson;
26 |
27 | /**
28 | * Create a {@link JsonParser} with a custom {@link Gson}.
29 | *
30 | * @param gson instance to use
31 | */
32 | public GsonJsonParser(Gson gson) {
33 | this.gson = gson;
34 | }
35 |
36 | /**
37 | * Create a default {@link JsonParser} instance.
38 | */
39 | public GsonJsonParser() {
40 | this(new GsonBuilder().setFieldNamingStrategy(FIELD_NAMING_STRATEGY).create());
41 | }
42 |
43 | @Override
44 | public String toJson(Object object) {
45 | return gson.toJson(object);
46 | }
47 |
48 | @Override
49 | public T fromJson(String json, Class clazz) {
50 | return gson.fromJson(json, clazz);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/gemini-tester/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | swiss.ameri
9 | gemini
10 | 1beta.0.2.8-SNAPSHOT
11 |
12 | gemini-tester
13 |
14 |
15 | 17
16 |
17 | ${project.version}
18 | swiss.ameri.gemini.tester
19 |
20 |
21 |
22 |
23 |
24 | swiss.ameri
25 | gemini-api
26 | ${gemini.version}
27 |
28 |
29 | swiss.ameri
30 | gemini-gson
31 | ${gemini.version}
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/gemini-tester/src/main/java/swiss/ameri/gemini/tester/GeminiTester.java:
--------------------------------------------------------------------------------
1 | package swiss.ameri.gemini.tester;
2 |
3 | import swiss.ameri.gemini.api.Content;
4 | import swiss.ameri.gemini.api.FunctionCall;
5 | import swiss.ameri.gemini.api.FunctionDeclaration;
6 | import swiss.ameri.gemini.api.FunctionResponse;
7 | import swiss.ameri.gemini.api.GenAi;
8 | import swiss.ameri.gemini.api.GenerationConfig;
9 | import swiss.ameri.gemini.api.GenerativeModel;
10 | import swiss.ameri.gemini.api.ModelVariant;
11 | import swiss.ameri.gemini.api.SafetySetting;
12 | import swiss.ameri.gemini.api.Schema;
13 | import swiss.ameri.gemini.gson.GsonJsonParser;
14 | import swiss.ameri.gemini.spi.JsonParser;
15 |
16 | import java.io.IOException;
17 | import java.io.InputStream;
18 | import java.util.Base64;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.concurrent.ExecutionException;
22 | import java.util.concurrent.TimeUnit;
23 | import java.util.concurrent.TimeoutException;
24 |
25 | /**
26 | * Example program to test the {@link GenAi} functionality.
27 | */
28 | public class GeminiTester {
29 |
30 | private GeminiTester() {
31 | throw new AssertionError("Not instantiable");
32 | }
33 |
34 | /**
35 | * Entry point. takes the Gemini API key as argument. See aistuio.google.com to generate a new API key.
36 | *
37 | * @param args should receive the API key as argument
38 | * @throws Exception if something goes wrong
39 | */
40 | public static void main(String[] args) throws Exception {
41 | JsonParser parser = new GsonJsonParser();
42 | String apiKey = args[0];
43 |
44 | try (var genAi = new GenAi(apiKey, parser)) {
45 | // each method represents an example usage
46 | listModels(genAi);
47 | getModel(genAi);
48 | countTokens(genAi);
49 | generateContent(genAi);
50 | generateContentStream(genAi);
51 | generateWithResponseSchema(genAi);
52 | generateContentStreamWithResponseSchema(genAi);
53 | multiChatTurn(genAi);
54 | textAndImage(genAi);
55 | embedContents(genAi);
56 | functionCall(genAi);
57 | functionResponse(genAi);
58 | }
59 |
60 |
61 | }
62 |
63 | private static void embedContents(GenAi genAi) {
64 | System.out.println("----- embed contents");
65 | var model = GenerativeModel.builder()
66 | .modelName(ModelVariant.TEXT_EMBEDDING_004)
67 | .addContent(Content.textContent(
68 | Content.Role.USER,
69 | "Write a 50 word story about a magic backpack."
70 | ))
71 | .addContent(Content.textContent(
72 | Content.Role.MODEL,
73 | "bla bla bla bla"
74 | ))
75 | .addSafetySetting(SafetySetting.of(
76 | SafetySetting.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
77 | SafetySetting.HarmBlockThreshold.BLOCK_ONLY_HIGH
78 | ))
79 | .generationConfig(new GenerationConfig(
80 | null,
81 | null,
82 | null,
83 | null,
84 | null,
85 | null,
86 | null
87 | ))
88 | .build();
89 |
90 | List embeddings = genAi.embedContents(model, null, null, null).join();
91 | System.out.println("Embedding count: " + embeddings.size());
92 | System.out.println("Values per embedding: " + embeddings.stream().map(GenAi.ContentEmbedding::values).map(List::size).toList());
93 |
94 | }
95 |
96 | private static void countTokens(GenAi genAi) {
97 | System.out.println("----- count tokens");
98 | var model = createStoryModel();
99 | Long result = genAi.countTokens(model)
100 | .join();
101 | System.out.println("Tokens: " + result);
102 | }
103 |
104 | private static void multiChatTurn(GenAi genAi) {
105 | System.out.println("----- multi turn chat");
106 | GenerativeModel chatModel = GenerativeModel.builder()
107 | .modelName(ModelVariant.GEMINI_1_5_PRO)
108 | .addContent(new Content.TextContent(
109 | Content.Role.USER.roleName(),
110 | "Write the first line of a story about a magic backpack."
111 | ))
112 | .addContent(new Content.TextContent(
113 | Content.Role.MODEL.roleName(),
114 | "In the bustling city of Meadow brook, lived a young girl named Sophie. She was a bright and curious soul with an imaginative mind."
115 | ))
116 | .addContent(new Content.TextContent(
117 | Content.Role.USER.roleName(),
118 | "Can you set it in a quiet village in 1600s France? Max 30 words"
119 | ))
120 | .build();
121 | genAi.generateContentStream(chatModel)
122 | .forEach(System.out::println);
123 | }
124 |
125 | private static void functionCall(GenAi genAi) throws ExecutionException, InterruptedException, TimeoutException {
126 | System.out.println("----- Function call");
127 | GenerativeModel chatModel = GenerativeModel.builder()
128 | .modelName(ModelVariant.GEMINI_1_5_PRO)
129 | .addContent(new Content.TextContent(
130 | Content.Role.USER.roleName(),
131 | "What is the current weather in Zurich?"
132 | ))
133 | .addFunctionDeclaration(new FunctionDeclaration(
134 | "getCurrentWeather",
135 | "Get the current weather for a city.",
136 | Schema.builder()
137 | .type(Schema.Type.OBJECT)
138 | .properties(Map.of("city", Schema.builder()
139 | .type(Schema.Type.STRING)
140 | .build()))
141 | .build()
142 | ))
143 | .build();
144 | genAi.generateContent(chatModel)
145 | .thenAccept(generatedContent -> {
146 | System.out.println(generatedContent);
147 | if (generatedContent.functionCall() == null) {
148 | throw new RuntimeException("Expected a function call...");
149 | }
150 | })
151 | .get(20, TimeUnit.SECONDS);
152 | }
153 |
154 | private static void functionResponse(GenAi genAi) throws ExecutionException, InterruptedException, TimeoutException {
155 | System.out.println("----- Function response");
156 | GenerativeModel chatModel = GenerativeModel.builder()
157 | .modelName(ModelVariant.GEMINI_1_5_PRO)
158 | .addContent(new Content.TextContent(
159 | Content.Role.USER.roleName(),
160 | "What is the current weather in Zurich?"
161 | ))
162 | .addContent(Content.functionCallContent(
163 | Content.Role.MODEL,
164 | new FunctionCall(
165 | "getCurrentWeather",
166 | null
167 | )
168 | ))
169 | .addContent(Content.functionResponseContent(
170 | Content.Role.USER,
171 | new FunctionResponse(
172 | "getCurrentWeather",
173 | Map.of("temperatureCelsius", "13")
174 | )
175 | ))
176 | .addFunctionDeclaration(new FunctionDeclaration(
177 | "getCurrentWeather",
178 | "Get the current weather for a city.",
179 | Schema.builder()
180 | .type(Schema.Type.OBJECT)
181 | .properties(Map.of("city", Schema.builder()
182 | .type(Schema.Type.STRING)
183 | .build()))
184 | .build()
185 | ))
186 | .build();
187 | genAi.generateContent(chatModel)
188 | .thenAccept(generatedContent -> {
189 | System.out.println(generatedContent);
190 | if (generatedContent.text() == null) {
191 | throw new RuntimeException("Expected a text...");
192 | }
193 | })
194 | .get(20, TimeUnit.SECONDS);
195 | }
196 |
197 | private static void generateContentStream(GenAi genAi) {
198 | System.out.println("----- Generate content (streaming) -- with usage meta data");
199 | var model = createStoryModel();
200 | genAi.generateContentStream(model)
201 | .forEach(x -> {
202 | System.out.println(x);
203 | // note that the usage metadata is updated as it arrives
204 | System.out.println(genAi.usageMetadata(x.id()));
205 | System.out.println(genAi.safetyRatings(x.id()));
206 | });
207 | }
208 |
209 | private static void generateContent(GenAi genAi) throws InterruptedException, ExecutionException, TimeoutException {
210 | var model = createStoryModel();
211 | System.out.println("----- Generate content (blocking)");
212 | genAi.generateContent(model)
213 | .thenAccept(gcr -> {
214 | System.out.println(gcr);
215 | System.out.println("----- Generate content (blocking) usage meta data & safety ratings");
216 | System.out.println(genAi.usageMetadata(gcr.id()));
217 | System.out.println(genAi.safetyRatings(gcr.id()).stream().map(GenAi.SafetyRating::toTypedSafetyRating).toList());
218 | })
219 | .get(20, TimeUnit.SECONDS);
220 | }
221 |
222 |
223 | private static void generateContentStreamWithResponseSchema(GenAi genAi) {
224 | System.out.println("----- Generate content (streaming) with response schema -- with usage meta data");
225 | var model = createResponseSchemaModel();
226 | genAi.generateContentStream(model)
227 | .forEach(x -> {
228 | System.out.println(x);
229 | // note that the usage metadata is updated as it arrives
230 | System.out.println(genAi.usageMetadata(x.id()));
231 | System.out.println(genAi.safetyRatings(x.id()));
232 | });
233 | }
234 |
235 | private static void generateWithResponseSchema(GenAi genAi) throws InterruptedException, ExecutionException, TimeoutException {
236 | var model = createResponseSchemaModel();
237 | System.out.println("----- Generate with response schema (blocking)");
238 | genAi.generateContent(model)
239 | .thenAccept(gcr -> {
240 | System.out.println(gcr);
241 | System.out.println("----- Generate with response schema (blocking) usage meta data & safety ratings");
242 | System.out.println(genAi.usageMetadata(gcr.id()));
243 | System.out.println(genAi.safetyRatings(gcr.id()).stream().map(GenAi.SafetyRating::toTypedSafetyRating).toList());
244 | })
245 | .get(20, TimeUnit.SECONDS);
246 | }
247 |
248 | private static GenerativeModel createResponseSchemaModel() {
249 | return GenerativeModel.builder()
250 | .modelName(ModelVariant.GEMINI_1_5_FLASH)
251 | .addContent(Content.textContent(
252 | Content.Role.USER,
253 | "List 3 popular cookie recipes."
254 | ))
255 | .addSafetySetting(SafetySetting.of(
256 | SafetySetting.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
257 | SafetySetting.HarmBlockThreshold.BLOCK_ONLY_HIGH
258 | ))
259 | .generationConfig(new GenerationConfig(
260 | null,
261 | "application/json",
262 | Schema.builder()
263 | .type(Schema.Type.ARRAY)
264 | .items(Schema.builder()
265 | .type(Schema.Type.OBJECT)
266 | .properties(Map.of(
267 | "recipe_name", Schema.builder()
268 | .type(Schema.Type.STRING)
269 | .build()
270 | ))
271 | .build())
272 | .build(),
273 | null,
274 | null,
275 | null,
276 | null
277 | ))
278 | .build();
279 | }
280 |
281 | private static GenerativeModel createStoryModel() {
282 | return GenerativeModel.builder()
283 | .modelName(ModelVariant.GEMINI_2_0_FLASH_EXP)
284 | .addContent(Content.textContent(
285 | Content.Role.USER,
286 | "Write a 50 word story about a magic backpack."
287 | ))
288 | .addSafetySetting(SafetySetting.of(
289 | SafetySetting.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
290 | SafetySetting.HarmBlockThreshold.BLOCK_ONLY_HIGH
291 | ))
292 | .generationConfig(new GenerationConfig(
293 | null,
294 | null,
295 | null,
296 | null,
297 | null,
298 | null,
299 | null
300 | ))
301 | .build();
302 | }
303 |
304 | private static void getModel(GenAi genAi) {
305 | System.out.println("----- Get Model");
306 | System.out.println(
307 | genAi.getModel(ModelVariant.GEMINI_1_5_PRO)
308 | );
309 | }
310 |
311 | private static void listModels(GenAi genAi) {
312 | System.out.println("----- List models");
313 | genAi.listModels()
314 | .forEach(System.out::println);
315 | }
316 |
317 | private static void textAndImage(GenAi genAi) throws IOException {
318 | System.out.println("----- text and image");
319 | var model = GenerativeModel.builder()
320 | .modelName(ModelVariant.GEMINI_1_5_FLASH)
321 | .addContent(
322 | Content.textAndMediaContentBuilder()
323 | .role(Content.Role.USER)
324 | .text("What is in this image?")
325 | .addMedia(new Content.MediaData(
326 | "image/png",
327 | loadSconesImage()
328 | ))
329 | .build()
330 | ).build();
331 | genAi.generateContent(model)
332 | .thenAccept(System.out::println)
333 | .join();
334 | }
335 |
336 | private static String loadSconesImage() throws IOException {
337 | try (InputStream is = GeminiTester.class.getClassLoader().getResourceAsStream("scones.png")) {
338 | if (is == null) {
339 | throw new IllegalStateException("Image not found! ");
340 | }
341 | return Base64.getEncoder().encodeToString(is.readAllBytes());
342 | }
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/gemini-tester/src/main/resources/scones.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michael-ameri/gemini-api/261f87c56e1a3b02a06611bdac5dedaeee759812/gemini-tester/src/main/resources/scones.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | swiss.ameri
8 | gemini
9 | 1beta.0.2.8-SNAPSHOT
10 | pom
11 |
12 |
13 |
14 | The Apache License, Version 2.0
15 | https://www.apache.org/licenses/LICENSE-2.0.txt
16 |
17 |
18 |
19 |
20 |
21 | Michael Ameri
22 | michael.ameri+gemini@gmail.com
23 |
24 |
25 |
26 |
27 | scm:git:git://github.com/michael-ameri/gemini-api.git
28 | scm:git:ssh://github.com:michael-ameri/gemini-api.git
29 | https://github.com/michael-ameri/gemini-api/tree/master
30 |
31 |
32 | https://github.com/michael-ameri/gemini-api
33 |
34 | Java library to access the gemini API.
35 |
36 |
37 |
38 | gemini-api
39 | gemini-gson
40 | gemini-tester
41 |
42 |
43 |
44 | 17
45 | UTF-8
46 |
47 | 0.4.0
48 | 3.2.4
49 | 3.3.1
50 | 3.4.2
51 | 3.7.0
52 |
53 | swiss.ameri.gemini
54 |
55 |
56 |
57 |
58 |
59 | org.apache.maven.plugins
60 | maven-jar-plugin
61 | ${maven-jar-plugin.version}
62 |
63 |
64 |
65 | ${module-name}
66 |
67 |
68 |
69 |
70 |
71 | org.apache.maven.plugins
72 | maven-source-plugin
73 | ${maven-source-plugin.version}
74 |
75 |
76 | attach-sources
77 | deploy
78 |
79 | jar-no-fork
80 |
81 |
82 |
83 |
84 |
85 | org.apache.maven.plugins
86 | maven-javadoc-plugin
87 | ${maven-javadoc-plugin.version}
88 |
89 |
90 | attach-javadocs
91 | deploy
92 |
93 | jar
94 |
95 |
96 |
97 |
98 | apiNote
99 | a
100 | API Note
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | org.apache.maven.plugins
109 | maven-gpg-plugin
110 | ${maven-gpg-plugin.version}
111 |
112 |
113 | sign-artifacts
114 | deploy
115 |
116 | sign
117 |
118 |
119 |
120 |
121 |
122 |
123 | org.sonatype.central
124 | central-publishing-maven-plugin
125 | ${central-publishing-maven-plugin.version}
126 | true
127 |
128 | central
129 | true
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------