Sprint 288: LibrarySymbolAdvisor (steps 1968-1972)
Specific API symbol recommendation layer. Five new headers: - LibrarySymbolRecord: functionName/signature/usagePattern/caveats - LibrarySymbolCatalog: multi-symbol registry per (library, domain) - LSPSymbolExtractor: parses LSP JSON into LibrarySymbolRecords - LibrarySymbolAdvisor: ranked symbols for (library, domain) alphabetically - Sprint288IntegrationSummary: CUDA+nlohmann catalog; cublasSgemm+json::parse verified 25/25 tests passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
52
editor/src/LSPSymbolExtractor.h
Normal file
52
editor/src/LSPSymbolExtractor.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
// Step 1970: LSPSymbolExtractor
|
||||
// Parses a simplified LSP-like JSON string into LibrarySymbolRecords.
|
||||
// Input format: {"symbols": [{"name": "...", "signature": "..."}]}
|
||||
// Requires nlohmann_json (available via vcpkg).
|
||||
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
class LSPSymbolExtractor {
|
||||
public:
|
||||
struct ExtractionResult {
|
||||
std::vector<LibrarySymbolRecord> records;
|
||||
bool success = true;
|
||||
std::string error;
|
||||
};
|
||||
|
||||
// Parse lspJson string and produce records for the given (libraryId, operationDomain).
|
||||
ExtractionResult extract(const std::string& libraryId,
|
||||
const std::string& operationDomain,
|
||||
const std::string& lspJson) const {
|
||||
try {
|
||||
auto j = nlohmann::json::parse(lspJson);
|
||||
if (!j.contains("symbols") || !j["symbols"].is_array()) {
|
||||
return {{}, false, "missing or non-array 'symbols' key"};
|
||||
}
|
||||
std::vector<LibrarySymbolRecord> records;
|
||||
for (const auto& sym : j["symbols"]) {
|
||||
LibrarySymbolRecord r;
|
||||
r.libraryId = libraryId;
|
||||
r.operationDomain = operationDomain;
|
||||
r.functionName = sym.value("name", std::string{});
|
||||
r.signature = sym.value("signature", std::string{});
|
||||
r.usagePattern = sym.value("usage", std::string{});
|
||||
if (sym.contains("caveats") && sym["caveats"].is_array()) {
|
||||
for (const auto& c : sym["caveats"])
|
||||
r.caveats.push_back(c.get<std::string>());
|
||||
}
|
||||
if (!r.functionName.empty()) records.push_back(std::move(r));
|
||||
}
|
||||
return {std::move(records), true, ""};
|
||||
} catch (const nlohmann::json::exception& e) {
|
||||
return {{}, false, std::string("json parse error: ") + e.what()};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
40
editor/src/LibrarySymbolAdvisor.h
Normal file
40
editor/src/LibrarySymbolAdvisor.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
// Step 1971: LibrarySymbolAdvisor
|
||||
// Given (libraryId, operationDomain, catalog) -> ranked symbol recommendations.
|
||||
// Ranking: exact domain match, sorted by functionName alphabetically.
|
||||
|
||||
#include "LibrarySymbolCatalog.h"
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
class LibrarySymbolAdvisor {
|
||||
public:
|
||||
// Returns symbols for (libraryId, operationDomain), sorted by functionName.
|
||||
// Empty if no symbols registered for this pair.
|
||||
std::vector<LibrarySymbolRecord> advise(const std::string& libraryId,
|
||||
const std::string& operationDomain,
|
||||
const LibrarySymbolCatalog& catalog) const {
|
||||
auto symbols = catalog.get(libraryId, operationDomain);
|
||||
std::sort(symbols.begin(), symbols.end(),
|
||||
[](const LibrarySymbolRecord& a, const LibrarySymbolRecord& b) {
|
||||
return a.functionName < b.functionName;
|
||||
});
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// Convenience: returns the top (first alphabetically) symbol, or nullopt.
|
||||
std::optional<LibrarySymbolRecord> topSymbol(const std::string& libraryId,
|
||||
const std::string& operationDomain,
|
||||
const LibrarySymbolCatalog& catalog) const {
|
||||
auto symbols = advise(libraryId, operationDomain, catalog);
|
||||
if (symbols.empty()) return std::nullopt;
|
||||
return symbols[0];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
68
editor/src/LibrarySymbolCatalog.h
Normal file
68
editor/src/LibrarySymbolCatalog.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
// Step 1969: LibrarySymbolCatalog
|
||||
// Registry of LibrarySymbolRecords. Multiple symbols per (libraryId, operationDomain).
|
||||
// Key: libraryId + "::" + operationDomain
|
||||
|
||||
#include "LibrarySymbolRecord.h"
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
class LibrarySymbolCatalog {
|
||||
public:
|
||||
void add(LibrarySymbolRecord record) {
|
||||
auto key = makeKey(record.libraryId, record.operationDomain);
|
||||
catalog_[key].push_back(std::move(record));
|
||||
}
|
||||
|
||||
// Returns all symbols for (libraryId, operationDomain); empty if none.
|
||||
std::vector<LibrarySymbolRecord> get(const std::string& libraryId,
|
||||
const std::string& operationDomain) const {
|
||||
auto it = catalog_.find(makeKey(libraryId, operationDomain));
|
||||
if (it == catalog_.end()) return {};
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool has(const std::string& libraryId, const std::string& operationDomain) const {
|
||||
auto it = catalog_.find(makeKey(libraryId, operationDomain));
|
||||
return it != catalog_.end() && !it->second.empty();
|
||||
}
|
||||
|
||||
// Total number of symbol records across all keys
|
||||
size_t size() const {
|
||||
size_t total = 0;
|
||||
for (const auto& [k, v] : catalog_) total += v.size();
|
||||
return total;
|
||||
}
|
||||
|
||||
std::vector<LibrarySymbolRecord> allForLibrary(const std::string& libraryId) const {
|
||||
std::vector<LibrarySymbolRecord> result;
|
||||
for (const auto& [k, v] : catalog_) {
|
||||
for (const auto& r : v) {
|
||||
if (r.libraryId == libraryId) result.push_back(r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<LibrarySymbolRecord> allForDomain(const std::string& operationDomain) const {
|
||||
std::vector<LibrarySymbolRecord> result;
|
||||
for (const auto& [k, v] : catalog_) {
|
||||
for (const auto& r : v) {
|
||||
if (r.operationDomain == operationDomain) result.push_back(r);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::string makeKey(const std::string& lib, const std::string& domain) {
|
||||
return lib + "::" + domain;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::vector<LibrarySymbolRecord>> catalog_;
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
19
editor/src/LibrarySymbolRecord.h
Normal file
19
editor/src/LibrarySymbolRecord.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
// Step 1968: LibrarySymbolRecord
|
||||
// Specific API symbol recommendation for a (library, operationDomain) pair.
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
struct LibrarySymbolRecord {
|
||||
std::string libraryId; // e.g. "cublas"
|
||||
std::string functionName; // e.g. "cublasSgemm"
|
||||
std::string signature; // e.g. "cublasStatus_t cublasSgemm(cublasHandle_t, ...)"
|
||||
std::string operationDomain; // e.g. "compute.matrix"
|
||||
std::string usagePattern; // example call showing typical invocation
|
||||
std::vector<std::string> caveats; // known footguns or preconditions
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
83
editor/src/Sprint288IntegrationSummary.h
Normal file
83
editor/src/Sprint288IntegrationSummary.h
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
// Step 1972: Sprint 288 Integration Summary
|
||||
// Steps 1968–1972: LibrarySymbolAdvisor
|
||||
//
|
||||
// 1968: LibrarySymbolRecord — functionName/signature/operationDomain/usagePattern/caveats
|
||||
// 1969: LibrarySymbolCatalog — multi-symbol registry per (library, domain)
|
||||
// 1970: LSPSymbolExtractor — parses LSP JSON into LibrarySymbolRecords
|
||||
// 1971: LibrarySymbolAdvisor — returns ranked symbols for (library, domain)
|
||||
// 1972: Integration — CUDA + nlohmann catalog, verify cublasSgemm + json::parse
|
||||
|
||||
#include "LibrarySymbolAdvisor.h"
|
||||
#include "LibrarySymbolCatalog.h"
|
||||
#include <string>
|
||||
|
||||
namespace whetstone {
|
||||
|
||||
struct Sprint288IntegrationSummary {
|
||||
int stepsCompleted = 5;
|
||||
bool success = true;
|
||||
std::string sprintName() const {
|
||||
return "Sprint 288: LibrarySymbolAdvisor";
|
||||
}
|
||||
|
||||
static LibrarySymbolCatalog buildSeedCatalog() {
|
||||
LibrarySymbolCatalog cat;
|
||||
|
||||
// cublas: compute.matrix
|
||||
cat.add({"cublas", "cublasSgemm",
|
||||
"cublasStatus_t cublasSgemm(cublasHandle_t, cublasOperation_t, cublasOperation_t,"
|
||||
" int m, int n, int k, const float* alpha, const float* A, int lda,"
|
||||
" const float* B, int ldb, const float* beta, float* C, int ldc)",
|
||||
"compute.matrix",
|
||||
"cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, A, lda, B, ldb, &beta, C, ldc)",
|
||||
{"requires-cublasCreate-before-use", "device-memory-only"}});
|
||||
cat.add({"cublas", "cublasDgemm",
|
||||
"cublasStatus_t cublasDgemm(cublasHandle_t, cublasOperation_t, cublasOperation_t,"
|
||||
" int m, int n, int k, const double* alpha, const double* A, int lda,"
|
||||
" const double* B, int ldb, const double* beta, double* C, int ldc)",
|
||||
"compute.matrix",
|
||||
"cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N, m, n, k, &alpha, A, lda, B, ldb, &beta, C, ldc)",
|
||||
{"double-precision", "requires-cublasCreate-before-use"}});
|
||||
|
||||
// nlohmann_json: serialization.json
|
||||
cat.add({"nlohmann_json", "json::dump",
|
||||
"std::string nlohmann::json::dump(int indent = -1) const",
|
||||
"serialization.json",
|
||||
"std::string s = j.dump(2); // pretty-print with indent=2",
|
||||
{}});
|
||||
cat.add({"nlohmann_json", "json::parse",
|
||||
"static nlohmann::json nlohmann::json::parse(const std::string& s)",
|
||||
"serialization.json",
|
||||
"auto j = nlohmann::json::parse(input_string);",
|
||||
{"throws-parse_error-on-invalid-json"}});
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
static bool verifyCUDALookup(const LibrarySymbolCatalog& cat) {
|
||||
LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("cublas", "compute.matrix", cat);
|
||||
if (syms.empty()) return false;
|
||||
// cublasDgemm < cublasSgemm alphabetically, so [0]=cublasDgemm, [1]=cublasSgemm
|
||||
bool foundSgemm = false;
|
||||
for (const auto& s : syms) {
|
||||
if (s.functionName == "cublasSgemm") { foundSgemm = true; break; }
|
||||
}
|
||||
return foundSgemm;
|
||||
}
|
||||
|
||||
static bool verifyNlohmannLookup(const LibrarySymbolCatalog& cat) {
|
||||
LibrarySymbolAdvisor adv;
|
||||
auto syms = adv.advise("nlohmann_json", "serialization.json", cat);
|
||||
if (syms.empty()) return false;
|
||||
// json::dump < json::parse alphabetically, so [0]=json::dump, [1]=json::parse
|
||||
bool foundParse = false;
|
||||
for (const auto& s : syms) {
|
||||
if (s.functionName == "json::parse") { foundParse = true; break; }
|
||||
}
|
||||
return foundParse;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace whetstone
|
||||
Reference in New Issue
Block a user