forked from bernardladenthin/java-llama.cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJsonParameters.java
More file actions
183 lines (168 loc) · 7.83 KB
/
Copy pathJsonParameters.java
File metadata and controls
183 lines (168 loc) · 7.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
// SPDX-FileCopyrightText: 2023-2025 Konstantin Herud
//
// SPDX-License-Identifier: MIT
package net.ladenthin.llama.parameters;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import lombok.EqualsAndHashCode;
import net.ladenthin.llama.args.CliArg;
import org.jspecify.annotations.Nullable;
/**
* Immutable base for JSON-shaped parameter builders.
*
* <p>The native server consumes parameters as a JSON object, so the type holds an
* unmodifiable {@code Map<String, String>} of pre-encoded value strings and a
* stateless {@link ParameterJsonSerializer}. Subclasses expose typed
* {@code withX(...)} methods that delegate to the protected {@code withScalar} /
* {@code withEnum} / {@code withOptionalJson} / {@code withRaw} helpers; each helper
* allocates a fresh map with one entry added or replaced and routes through the
* abstract {@link #withParameters(Map)} factory hook so the subclass returns a new
* instance of its own concrete type.
*
* <p>{@code equals}/{@code hashCode} are generated by Lombok over the {@code parameters}
* map. {@code toString} is intentionally handwritten (not Lombok-generated): it emits an
* actual JSON object string of the accumulated parameters and is consumed by callers
* that hand the result to the native server. The {@code serializer} field is excluded
* from equality because it is a stateless helper instance (all instances of the same
* class are functionally equivalent).
*/
@EqualsAndHashCode
abstract class JsonParameters {
// Stored as a pre-encoded String map so the native (JSON-based) server can read
// the value verbatim. The map is wrapped in Collections.unmodifiableMap by every
// factory hook so even reflective access cannot mutate stored state.
final Map<String, String> parameters;
/** Serializer for converting Java values to JSON-safe strings. */
@EqualsAndHashCode.Exclude
protected final ParameterJsonSerializer serializer = new ParameterJsonSerializer();
/** Construct an empty parameter set. Subclasses chain factories on top of this. */
protected JsonParameters() {
this.parameters = Collections.emptyMap();
}
/**
* Wrap a caller-provided map verbatim. The caller is responsible for ensuring
* the map is already unmodifiable (the {@code withX} helpers always wrap before
* calling); this constructor does not re-wrap.
*
* @param parameters the pre-built parameter map; must already be unmodifiable
*/
protected JsonParameters(Map<String, String> parameters) {
this.parameters = parameters;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{\n");
int i = 0;
for (Map.Entry<String, String> entry : parameters.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
builder.append("\t\"").append(key).append("\": ").append(value);
if (i++ < parameters.size() - 1) {
builder.append(',');
}
builder.append('\n');
}
builder.append('}');
return builder.toString();
}
/**
* Serialize a non-null string to its JSON string form. Use
* {@link #withOptionalJson(String, String)} when the input may be null and the
* caller wants null to behave as "do not set this parameter".
*
* @param text the non-null input
* @return the JSON-encoded string
*/
String toJsonString(String text) {
return serializer.toJsonString(text);
}
/**
* Subclass factory hook. Return a new instance of the concrete subtype carrying
* the supplied (already unmodifiable) parameter map; the existing instance is
* left untouched.
*
* @param newParameters the new parameter map (must already be unmodifiable)
* @param <T> the concrete subtype of this parameter set
* @return a new instance of the concrete subtype
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
protected abstract <T extends JsonParameters> T withParameters(Map<String, String> newParameters);
/**
* Internal helper that copies the current map, applies one {@code put}, wraps the
* copy as unmodifiable and routes through {@link #withParameters(Map)}.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
private <T extends JsonParameters> T withPut(String key, String value) {
Map<String, String> next = new HashMap<>(parameters);
next.put(key, value);
return withParameters(Collections.unmodifiableMap(next));
}
/**
* Returns a new parameter set with {@code key} mapped to the pre-JSON-encoded
* raw string verbatim. Used when the caller has already built a JSON fragment
* (arrays, objects) externally.
*
* @param key the parameter key
* @param value the raw, already-encoded value
* @param <T> the concrete subtype of this parameter set
* @return a new instance with the entry inserted or replaced
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
protected final <T extends JsonParameters> T withRaw(String key, String value) {
return withPut(key, value);
}
/**
* Returns a new parameter set with {@code key} mapped to {@code value} via
* {@link String#valueOf(Object)}. Used for primitives (int, long, float, double,
* boolean).
*
* @param key the parameter key
* @param value the scalar value; autoboxed at the call site
* @param <T> the concrete subtype of this parameter set
* @return a new instance with the entry inserted or replaced
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
protected final <T extends JsonParameters> T withScalar(String key, Object value) {
// String.valueOf on a non-finite float/double yields "NaN"/"Infinity" — invalid JSON tokens
// the native nlohmann parser rejects. Reject at the source so the caller gets a clear error
// instead of an opaque downstream failure. Integer/Long are always finite (a no-op here);
// Boolean is not a Number and is skipped.
if (value instanceof Number && !Double.isFinite(((Number) value).doubleValue())) {
throw new IllegalArgumentException(key + " must be a finite number but was " + value);
}
return withPut(key, String.valueOf(value));
}
/**
* Returns a new parameter set with {@code key} mapped to the CLI-argument string
* of the given enum constant.
*
* @param key the parameter key
* @param value the enum constant; must implement {@link net.ladenthin.llama.args.CliArg}
* @param <T> the concrete subtype of this parameter set
* @return a new instance with the entry inserted or replaced
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
protected final <T extends JsonParameters> T withEnum(String key, CliArg value) {
return withPut(key, value.getArgValue());
}
/**
* Conditionally store a JSON-encoded string under {@code key}: when {@code text}
* is {@code null} the call is a no-op (returns {@code this}); otherwise the value
* is JSON-encoded and a new instance is returned.
*
* @param key the parameter key
* @param text the optional input; {@code null} means "leave the parameter unset"
* @param <T> the concrete subtype of this parameter set
* @return {@code this} if {@code text} is null, otherwise a new instance with the entry set
*/
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
protected final <T extends JsonParameters> T withOptionalJson(String key, @Nullable String text) {
if (text == null) {
return (T) this;
}
return withPut(key, serializer.toJsonString(text));
}
}