Skip to content

Commit 6c4f8da

Browse files
Merge pull request #14 from lucaromagnoli/fix/llmtypes
Remove business logic coupling from core types
2 parents be44712 + a98d537 commit 6c4f8da

File tree

5 files changed

+46
-276
lines changed

5 files changed

+46
-276
lines changed

.github/workflows/ci.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,51 @@ jobs:
1919
- name: Checkout code
2020
uses: actions/checkout@v4
2121

22+
# Cache vcpkg packages (Windows)
23+
- name: Cache vcpkg packages
24+
if: matrix.os == 'windows-latest'
25+
uses: actions/cache@v4
26+
with:
27+
path: |
28+
${{ github.workspace }}/vcpkg
29+
${{ github.workspace }}/vcpkg_installed
30+
key: vcpkg-${{ matrix.os }}-${{ hashFiles('vcpkg.json') }}
31+
restore-keys: |
32+
vcpkg-${{ matrix.os }}-
33+
34+
# Cache CMake build directory
35+
- name: Cache CMake build
36+
uses: actions/cache@v4
37+
with:
38+
path: |
39+
build
40+
!build/tests/llmcpp_tests
41+
!build/examples
42+
key: cmake-${{ matrix.os }}-${{ matrix.build_type }}-${{ hashFiles('CMakeLists.txt', '**/CMakeLists.txt') }}
43+
restore-keys: |
44+
cmake-${{ matrix.os }}-${{ matrix.build_type }}-
45+
46+
# Cache package manager dependencies
47+
- name: Cache Homebrew (macOS)
48+
if: matrix.os == 'macos-latest'
49+
uses: actions/cache@v4
50+
with:
51+
path: |
52+
~/Library/Caches/Homebrew
53+
/opt/homebrew/Cellar
54+
key: homebrew-${{ hashFiles('.github/workflows/ci.yml') }}
55+
restore-keys: homebrew-
56+
57+
- name: Cache apt packages (Linux)
58+
if: matrix.os == 'ubuntu-22.04'
59+
uses: actions/cache@v4
60+
with:
61+
path: |
62+
/var/cache/apt
63+
/var/lib/apt
64+
key: apt-${{ hashFiles('.github/workflows/ci.yml') }}
65+
restore-keys: apt-
66+
2267
- name: Install dependencies (macOS)
2368
if: matrix.os == 'macos-latest'
2469
run: brew install cmake ninja
@@ -63,7 +108,7 @@ jobs:
63108
64109
- name: Build
65110
timeout-minutes: 15
66-
run: cmake --build build --config ${{ matrix.build_type }}
111+
run: cmake --build build --config ${{ matrix.build_type }} --parallel
67112

68113
- name: Test
69114
working-directory: build

include/core/LLMTypes.h

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,72 +10,6 @@ using json = nlohmann::json;
1010
// Input type using standard C++ vectors instead of JUCE StringArray
1111
using LLMInput = std::vector<std::string>;
1212

13-
/**
14-
* @brief Common schema names for structured outputs
15-
*
16-
* Provides type-safe schema naming to prevent typos and improve maintainability.
17-
* These correspond to common use cases in AI applications.
18-
*/
19-
enum class ResponsesSchemaName {
20-
SentimentAnalysis, // sentiment_analysis
21-
DataExtraction, // data_extraction
22-
Classification, // classification
23-
EntityExtraction, // entity_extraction
24-
ContentAnalysis, // content_analysis
25-
DocumentAnalysis, // document_analysis
26-
ReviewAnalysis, // review_analysis
27-
WeatherInfo, // weather_info
28-
ProductInfo, // product_info
29-
UserProfile, // user_profile
30-
ImageAnalysis, // image_analysis
31-
TextSummary, // text_summary
32-
Translation, // translation
33-
MathSolution, // math_solution
34-
CodeAnalysis, // code_analysis
35-
Custom // For user-defined schema names
36-
};
37-
38-
/**
39-
* @brief Convert ResponsesSchemaName enum to string
40-
*/
41-
inline std::string toString(ResponsesSchemaName schemaName) {
42-
switch (schemaName) {
43-
case ResponsesSchemaName::SentimentAnalysis:
44-
return "sentiment_analysis";
45-
case ResponsesSchemaName::DataExtraction:
46-
return "data_extraction";
47-
case ResponsesSchemaName::Classification:
48-
return "classification";
49-
case ResponsesSchemaName::EntityExtraction:
50-
return "entity_extraction";
51-
case ResponsesSchemaName::ContentAnalysis:
52-
return "content_analysis";
53-
case ResponsesSchemaName::DocumentAnalysis:
54-
return "document_analysis";
55-
case ResponsesSchemaName::ReviewAnalysis:
56-
return "review_analysis";
57-
case ResponsesSchemaName::WeatherInfo:
58-
return "weather_info";
59-
case ResponsesSchemaName::ProductInfo:
60-
return "product_info";
61-
case ResponsesSchemaName::UserProfile:
62-
return "user_profile";
63-
case ResponsesSchemaName::ImageAnalysis:
64-
return "image_analysis";
65-
case ResponsesSchemaName::TextSummary:
66-
return "text_summary";
67-
case ResponsesSchemaName::Translation:
68-
return "translation";
69-
case ResponsesSchemaName::MathSolution:
70-
return "math_solution";
71-
case ResponsesSchemaName::CodeAnalysis:
72-
return "code_analysis";
73-
case ResponsesSchemaName::Custom:
74-
return "custom_schema";
75-
}
76-
return "unknown_schema";
77-
}
78-
7913
/// Represents the configuration for the LLM
8014
struct LLMRequestConfig {
8115
std::string client;

include/openai/OpenAISchemaBuilder.h

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -87,40 +87,3 @@ class OpenAIChatSchemaBuilder {
8787
std::string schemaName_;
8888
JsonSchemaBuilder schema_;
8989
};
90-
91-
/**
92-
* @brief Common schema patterns for OpenAI APIs
93-
*
94-
* Provides pre-built schemas for common use cases.
95-
*/
96-
class OpenAISchemaPatterns {
97-
public:
98-
// Sentiment analysis schema
99-
static OpenAI::TextOutputConfig sentimentAnalysis();
100-
101-
// Data extraction schema
102-
static OpenAI::TextOutputConfig dataExtraction(const std::vector<std::string>& fields);
103-
104-
// Classification schema
105-
static OpenAI::TextOutputConfig classification(const std::vector<std::string>& categories);
106-
107-
// Summary schema
108-
static OpenAI::TextOutputConfig summary(int maxLength = 500);
109-
110-
// Key-value extraction
111-
static OpenAI::TextOutputConfig keyValueExtraction();
112-
113-
// Boolean decision
114-
static OpenAI::TextOutputConfig booleanDecision(const std::string& question);
115-
116-
// Structured entity extraction
117-
static OpenAI::TextOutputConfig entityExtraction();
118-
119-
// Translation schema
120-
static OpenAI::TextOutputConfig translation(const std::string& targetLanguage);
121-
122-
// Chat completions patterns
123-
static json chatJsonMode();
124-
static json chatClassification(const std::vector<std::string>& categories);
125-
static json chatSentiment();
126-
};

src/openai/OpenAISchemaBuilder.cpp

Lines changed: 0 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -73,132 +73,3 @@ json OpenAIChatSchemaBuilder::build() const {
7373
}
7474
return json{{"type", "text"}}; // fallback
7575
}
76-
77-
// OpenAISchemaPatterns implementation
78-
OpenAI::TextOutputConfig OpenAISchemaPatterns::sentimentAnalysis() {
79-
return OpenAIResponsesSchemaBuilder("sentiment_analysis")
80-
.description("Analyze the sentiment of the given text")
81-
.property("sentiment", JsonSchemaBuilder::stringEnum({"positive", "negative", "neutral"}))
82-
.property("confidence", JsonSchemaBuilder::number().minimum(0).maximum(1).description(
83-
"Confidence score from 0 to 1"))
84-
.required({"sentiment", "confidence"})
85-
.build();
86-
}
87-
88-
OpenAI::TextOutputConfig OpenAISchemaPatterns::dataExtraction(
89-
const std::vector<std::string>& fields) {
90-
auto builder = OpenAIResponsesSchemaBuilder("data_extraction")
91-
.description("Extract structured data from text");
92-
93-
for (const auto& field : fields) {
94-
builder.property(field, JsonSchemaBuilder::string().description("Extracted " + field));
95-
}
96-
97-
builder.required(fields);
98-
return builder.build();
99-
}
100-
101-
OpenAI::TextOutputConfig OpenAISchemaPatterns::classification(
102-
const std::vector<std::string>& categories) {
103-
return OpenAIResponsesSchemaBuilder("classification")
104-
.description("Classify the input into one of the predefined categories")
105-
.property("category", JsonSchemaBuilder::stringEnum(categories))
106-
.property("confidence", JsonSchemaBuilder::number().minimum(0).maximum(1))
107-
.property("reasoning", JsonSchemaBuilder::string().description(
108-
"Brief explanation of the classification"))
109-
.required({"category", "confidence"})
110-
.build();
111-
}
112-
113-
OpenAI::TextOutputConfig OpenAISchemaPatterns::summary(int maxLength) {
114-
return OpenAIResponsesSchemaBuilder("summary")
115-
.description("Generate a concise summary of the input text")
116-
.property("summary",
117-
JsonSchemaBuilder::string().maxLength(maxLength).description("Concise summary"))
118-
.property(
119-
"key_points",
120-
JsonSchemaBuilder::arrayOf(JsonSchemaBuilder::string()).description("Main points"))
121-
.property("word_count",
122-
JsonSchemaBuilder::integer().minimum(1).description("Number of words in summary"))
123-
.required({"summary", "key_points", "word_count"})
124-
.build();
125-
}
126-
127-
OpenAI::TextOutputConfig OpenAISchemaPatterns::keyValueExtraction() {
128-
return OpenAIResponsesSchemaBuilder("key_value_extraction")
129-
.description("Extract key-value pairs from the text")
130-
.property("extracted_data",
131-
JsonSchemaBuilder::object().additionalProperties(JsonSchemaBuilder::string()))
132-
.property("metadata", JsonSchemaBuilder::object()
133-
.property("extraction_confidence",
134-
JsonSchemaBuilder::number().minimum(0).maximum(1))
135-
.property("total_pairs", JsonSchemaBuilder::integer().minimum(0)))
136-
.required({"extracted_data", "metadata"})
137-
.build();
138-
}
139-
140-
OpenAI::TextOutputConfig OpenAISchemaPatterns::booleanDecision(const std::string& question) {
141-
return OpenAIResponsesSchemaBuilder("boolean_decision")
142-
.description("Make a yes/no decision based on: " + question)
143-
.property("decision",
144-
JsonSchemaBuilder::boolean().description("True for yes, false for no"))
145-
.property("reasoning",
146-
JsonSchemaBuilder::string().description("Explanation for the decision"))
147-
.property("confidence", JsonSchemaBuilder::number().minimum(0).maximum(1))
148-
.required({"decision", "reasoning", "confidence"})
149-
.build();
150-
}
151-
152-
OpenAI::TextOutputConfig OpenAISchemaPatterns::entityExtraction() {
153-
auto entitySchema =
154-
JsonSchemaBuilder::object()
155-
.property("text", JsonSchemaBuilder::string().description("The extracted entity text"))
156-
.property("type", JsonSchemaBuilder::stringEnum(
157-
{"PERSON", "ORGANIZATION", "LOCATION", "DATE", "MONEY", "OTHER"}))
158-
.property("start_pos",
159-
JsonSchemaBuilder::integer().minimum(0).description("Start position in text"))
160-
.property("end_pos",
161-
JsonSchemaBuilder::integer().minimum(0).description("End position in text"))
162-
.required({"text", "type"});
163-
164-
return OpenAIResponsesSchemaBuilder("entity_extraction")
165-
.description("Extract named entities from the text")
166-
.property("entities", JsonSchemaBuilder::arrayOf(entitySchema))
167-
.property("entity_count", JsonSchemaBuilder::integer().minimum(0))
168-
.required({"entities", "entity_count"})
169-
.build();
170-
}
171-
172-
OpenAI::TextOutputConfig OpenAISchemaPatterns::translation(const std::string& targetLanguage) {
173-
return OpenAIResponsesSchemaBuilder("translation")
174-
.description("Translate text to " + targetLanguage)
175-
.property("translated_text", JsonSchemaBuilder::string().description("The translated text"))
176-
.property("source_language",
177-
JsonSchemaBuilder::string().description("Detected source language"))
178-
.property("target_language", JsonSchemaBuilder::string().constValue(targetLanguage))
179-
.property("confidence", JsonSchemaBuilder::number().minimum(0).maximum(1))
180-
.required({"translated_text", "source_language", "target_language", "confidence"})
181-
.build();
182-
}
183-
184-
// Chat completions patterns
185-
json OpenAISchemaPatterns::chatJsonMode() { return OpenAIChatSchemaBuilder().jsonMode().build(); }
186-
187-
json OpenAISchemaPatterns::chatClassification(const std::vector<std::string>& categories) {
188-
auto schema = JsonSchemaBuilder::object()
189-
.property("category", JsonSchemaBuilder::stringEnum(categories))
190-
.property("confidence", JsonSchemaBuilder::number().minimum(0).maximum(1))
191-
.required({"category", "confidence"});
192-
193-
return OpenAIChatSchemaBuilder().jsonSchema("classification", schema).build();
194-
}
195-
196-
json OpenAISchemaPatterns::chatSentiment() {
197-
auto schema = JsonSchemaBuilder::object()
198-
.property("sentiment",
199-
JsonSchemaBuilder::stringEnum({"positive", "negative", "neutral"}))
200-
.property("confidence", JsonSchemaBuilder::number().minimum(0).maximum(1))
201-
.required({"sentiment", "confidence"});
202-
203-
return OpenAIChatSchemaBuilder().jsonSchema("sentiment", schema).build();
204-
}

tests/unit/test_json_schema_builder.cpp

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -174,49 +174,6 @@ TEST_CASE("OpenAI Chat Completions schema builder", "[schema][openai][chat]") {
174174
}
175175
}
176176

177-
TEST_CASE("OpenAI schema patterns", "[schema][openai][patterns]") {
178-
SECTION("Sentiment analysis pattern") {
179-
auto config = OpenAISchemaPatterns::sentimentAnalysis();
180-
auto configJson = config.toJson();
181-
182-
REQUIRE(configJson["format"]["name"] == "sentiment_analysis");
183-
auto schema = configJson["format"]["schema"];
184-
REQUIRE(schema["properties"].contains("sentiment"));
185-
REQUIRE(schema["properties"].contains("confidence"));
186-
}
187-
188-
SECTION("Classification pattern") {
189-
std::vector<std::string> categories = {"technology", "science", "arts", "sports"};
190-
auto config = OpenAISchemaPatterns::classification(categories);
191-
auto configJson = config.toJson();
192-
193-
auto schema = configJson["format"]["schema"];
194-
REQUIRE(schema["properties"]["category"]["enum"].size() == 4);
195-
REQUIRE(schema["properties"]["category"]["enum"][0] == "technology");
196-
}
197-
198-
SECTION("Data extraction pattern") {
199-
std::vector<std::string> fields = {"name", "company", "position"};
200-
auto config = OpenAISchemaPatterns::dataExtraction(fields);
201-
auto configJson = config.toJson();
202-
203-
auto schema = configJson["format"]["schema"];
204-
REQUIRE(schema["properties"].contains("name"));
205-
REQUIRE(schema["properties"].contains("company"));
206-
REQUIRE(schema["properties"].contains("position"));
207-
REQUIRE(schema["required"].size() == 3);
208-
}
209-
210-
SECTION("Chat patterns") {
211-
auto jsonMode = OpenAISchemaPatterns::chatJsonMode();
212-
REQUIRE(jsonMode["type"] == "json_object");
213-
214-
auto chatSentiment = OpenAISchemaPatterns::chatSentiment();
215-
REQUIRE(chatSentiment["type"] == "json_schema");
216-
REQUIRE(chatSentiment["json_schema"]["name"] == "sentiment");
217-
}
218-
}
219-
220177
TEST_CASE("Schema integration examples", "[schema][integration]") {
221178
SECTION("Complex nested schema for content analysis") {
222179
auto entitySchema =

0 commit comments

Comments
 (0)