autotype 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +8 -0
- data/LICENSE +21 -0
- data/README.md +93 -0
- data/Rakefile +21 -0
- data/autotype.example.yml +19 -0
- data/autotype.gemspec +38 -0
- data/exe/autotype +6 -0
- data/ext/autotype/Makefile +273 -0
- data/ext/autotype/autotype.bundle.dSYM/Contents/Info.plist +20 -0
- data/ext/autotype/autotype.bundle.dSYM/Contents/Resources/Relocations/aarch64/autotype.bundle.yml +5 -0
- data/ext/autotype/autotype.c +277 -0
- data/ext/autotype/extconf.rb +15 -0
- data/lib/autotype/discovery_profile.rb +527 -0
- data/lib/autotype/engine.rb +4613 -0
- data/lib/autotype/native.rb +34 -0
- data/lib/autotype/native_bridge.rb +121 -0
- data/lib/autotype/profile.rb +46 -0
- data/lib/autotype/type_string.rb +48 -0
- data/lib/autotype/version.rb +5 -0
- data/lib/autotype.rb +15 -0
- data/native/autotype/Makefile +23 -0
- data/native/autotype/include/stc.h +107 -0
- data/native/autotype/src/main.c +50 -0
- data/native/autotype/src/solver.c +296 -0
- data/native/autotype/src/type.c +223 -0
- data/spec/autotype_config_spec.rb +91 -0
- data/spec/autotype_native_spec.rb +71 -0
- data/spec/autotype_spec.rb +1028 -0
- data/spec/examples.txt +72 -0
- data/spec/fixtures/autotype.yml +9 -0
- data/spec/fixtures/entities/tool_call.rb +7 -0
- data/spec/fixtures/entities/tool_result.rb +7 -0
- data/spec/fixtures/pipeline/actors/demo_actor.rb +8 -0
- data/spec/spec_helper.rb +25 -0
- metadata +149 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Ruby bindings for the native Autotype solver core.
|
|
3
|
+
*
|
|
4
|
+
* Exposes AutotypeNative.solve_graph for the fixed-point builtin
|
|
5
|
+
* solver. Collection and rendering remain in Ruby.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
#include <ruby.h>
|
|
9
|
+
#include "../../native/autotype/include/stc.h"
|
|
10
|
+
|
|
11
|
+
extern stc_solver *stc_solver_new(void);
|
|
12
|
+
extern void stc_solver_free(stc_solver *solver);
|
|
13
|
+
extern void stc_solver_load_flat(stc_solver *solver, stc_capability *capabilities, size_t capability_count);
|
|
14
|
+
extern int stc_solver_run(stc_solver *solver);
|
|
15
|
+
extern stc_type *stc_resolve(stc_solver *solver, stc_type *type);
|
|
16
|
+
|
|
17
|
+
static VALUE stc_mAutotypeNative;
|
|
18
|
+
static VALUE stc_cSolver;
|
|
19
|
+
|
|
20
|
+
static VALUE stc_hash_get(VALUE hash, const char *key) {
|
|
21
|
+
VALUE sym = ID2SYM(rb_intern(key));
|
|
22
|
+
VALUE val = rb_hash_aref(hash, sym);
|
|
23
|
+
if (!NIL_P(val)) return val;
|
|
24
|
+
return rb_hash_aref(hash, rb_str_new2(key));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static stc_var_id stc_var_id_from_ruby(VALUE value) {
|
|
28
|
+
return (stc_var_id)NUM2ULL(value);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static VALUE stc_var_id_to_ruby(stc_var_id id) {
|
|
32
|
+
return ULL2NUM((unsigned long long)id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static stc_type *stc_type_from_ruby(stc_solver *solver, VALUE hash) {
|
|
36
|
+
VALUE kind = stc_hash_get(hash, "k");
|
|
37
|
+
Check_Type(kind, T_STRING);
|
|
38
|
+
const char *tag = StringValueCStr(kind);
|
|
39
|
+
|
|
40
|
+
if (strcmp(tag, "named") == 0) {
|
|
41
|
+
VALUE name = stc_hash_get(hash, "name");
|
|
42
|
+
return stc_type_named(solver, StringValueCStr(name));
|
|
43
|
+
}
|
|
44
|
+
if (strcmp(tag, "var") == 0) {
|
|
45
|
+
VALUE id = stc_hash_get(hash, "id");
|
|
46
|
+
VALUE hint = stc_hash_get(hash, "hint");
|
|
47
|
+
const char *hint_str = NIL_P(hint) ? NULL : StringValueCStr(hint);
|
|
48
|
+
return stc_type_var(solver, stc_var_id_from_ruby(id), hint_str);
|
|
49
|
+
}
|
|
50
|
+
if (strcmp(tag, "generic") == 0) {
|
|
51
|
+
VALUE name = stc_hash_get(hash, "name");
|
|
52
|
+
VALUE args = stc_hash_get(hash, "args");
|
|
53
|
+
long argc = RARRAY_LEN(args);
|
|
54
|
+
stc_type **typed_args = calloc((size_t)argc, sizeof(stc_type *));
|
|
55
|
+
if (!typed_args) return NULL;
|
|
56
|
+
for (long i = 0; i < argc; i++) {
|
|
57
|
+
typed_args[i] = stc_type_from_ruby(solver, rb_ary_entry(args, i));
|
|
58
|
+
}
|
|
59
|
+
return stc_type_generic(solver, StringValueCStr(name), typed_args, (size_t)argc);
|
|
60
|
+
}
|
|
61
|
+
if (strcmp(tag, "union") == 0) {
|
|
62
|
+
VALUE members = stc_hash_get(hash, "members");
|
|
63
|
+
long count = RARRAY_LEN(members);
|
|
64
|
+
stc_type **typed_members = calloc((size_t)count, sizeof(stc_type *));
|
|
65
|
+
if (!typed_members) return NULL;
|
|
66
|
+
for (long i = 0; i < count; i++) {
|
|
67
|
+
typed_members[i] = stc_type_from_ruby(solver, rb_ary_entry(members, i));
|
|
68
|
+
}
|
|
69
|
+
return stc_type_union(solver, typed_members, (size_t)count);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return stc_type_named(solver, "Object");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
static VALUE stc_type_to_ruby_inner(stc_type *type, stc_var_id *seen, size_t *seen_count, int depth);
|
|
76
|
+
|
|
77
|
+
static VALUE stc_type_to_ruby(stc_type *type) {
|
|
78
|
+
stc_var_id seen[64];
|
|
79
|
+
size_t seen_count = 0;
|
|
80
|
+
return stc_type_to_ruby_inner(type, seen, &seen_count, 0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static VALUE stc_type_to_ruby_inner(stc_type *type, stc_var_id *seen, size_t *seen_count, int depth) {
|
|
84
|
+
VALUE hash = rb_hash_new();
|
|
85
|
+
if (!type || depth > 24) {
|
|
86
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("named"));
|
|
87
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("name")), rb_str_new2("Object"));
|
|
88
|
+
return hash;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (type->kind == STC_TY_VAR) {
|
|
92
|
+
for (size_t i = 0; i < *seen_count; i++) {
|
|
93
|
+
if (seen[i] == type->as.var.id) {
|
|
94
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("var"));
|
|
95
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("id")), stc_var_id_to_ruby(type->as.var.id));
|
|
96
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("hint")), rb_str_new2(type->as.var.hint));
|
|
97
|
+
return hash;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (*seen_count < 64) seen[(*seen_count)++] = type->as.var.id;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
switch (type->kind) {
|
|
104
|
+
case STC_TY_NAMED:
|
|
105
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("named"));
|
|
106
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("name")), rb_str_new2(type->as.named.name));
|
|
107
|
+
break;
|
|
108
|
+
case STC_TY_VAR:
|
|
109
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("var"));
|
|
110
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("id")), stc_var_id_to_ruby(type->as.var.id));
|
|
111
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("hint")), rb_str_new2(type->as.var.hint));
|
|
112
|
+
break;
|
|
113
|
+
case STC_TY_GENERIC: {
|
|
114
|
+
VALUE args = rb_ary_new_capa((long)type->as.generic.arg_count);
|
|
115
|
+
for (size_t i = 0; i < type->as.generic.arg_count; i++) {
|
|
116
|
+
rb_ary_push(args, stc_type_to_ruby_inner(type->as.generic.args[i], seen, seen_count, depth + 1));
|
|
117
|
+
}
|
|
118
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("generic"));
|
|
119
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("name")), rb_str_new2(type->as.generic.name));
|
|
120
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("args")), args);
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case STC_TY_UNION: {
|
|
124
|
+
VALUE members = rb_ary_new_capa((long)type->as.union_.member_count);
|
|
125
|
+
for (size_t i = 0; i < type->as.union_.member_count; i++) {
|
|
126
|
+
rb_ary_push(members, stc_type_to_ruby_inner(type->as.union_.members[i], seen, seen_count, depth + 1));
|
|
127
|
+
}
|
|
128
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("union"));
|
|
129
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("members")), members);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
default:
|
|
133
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("k")), rb_str_new2("named"));
|
|
134
|
+
rb_hash_aset(hash, ID2SYM(rb_intern("name")), rb_str_new2("Object"));
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
return hash;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static stc_capability *stc_load_capabilities(stc_solver *solver, VALUE capabilities, size_t *out_count) {
|
|
141
|
+
long count = RARRAY_LEN(capabilities);
|
|
142
|
+
stc_capability *caps = calloc((size_t)count, sizeof(stc_capability));
|
|
143
|
+
if (!caps) return NULL;
|
|
144
|
+
|
|
145
|
+
for (long i = 0; i < count; i++) {
|
|
146
|
+
VALUE entry = rb_ary_entry(capabilities, i);
|
|
147
|
+
stc_capability *cap = &caps[i];
|
|
148
|
+
|
|
149
|
+
VALUE message = stc_hash_get(entry, "message");
|
|
150
|
+
VALUE receiver = stc_hash_get(entry, "receiver");
|
|
151
|
+
VALUE result_id = stc_hash_get(entry, "result");
|
|
152
|
+
VALUE args = stc_hash_get(entry, "args");
|
|
153
|
+
VALUE line = stc_hash_get(entry, "line");
|
|
154
|
+
|
|
155
|
+
cap->receiver = stc_type_from_ruby(solver, receiver);
|
|
156
|
+
cap->result = stc_type_var(solver, stc_var_id_from_ruby(result_id), "result");
|
|
157
|
+
strncpy(cap->message, StringValueCStr(message), sizeof(cap->message) - 1);
|
|
158
|
+
cap->line = NIL_P(line) ? 0 : (int32_t)NUM2LONG(line);
|
|
159
|
+
|
|
160
|
+
long argc = RARRAY_LEN(args);
|
|
161
|
+
if (argc > 0) {
|
|
162
|
+
cap->args = calloc((size_t)argc, sizeof(stc_type *));
|
|
163
|
+
cap->arg_count = (size_t)argc;
|
|
164
|
+
for (long a = 0; a < argc; a++) {
|
|
165
|
+
cap->args[a] = stc_type_from_ruby(solver, rb_ary_entry(args, a));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
*out_count = (size_t)count;
|
|
171
|
+
return caps;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static int stc_load_binding_iter(VALUE key, VALUE value, VALUE arg) {
|
|
175
|
+
stc_solver *solver = (stc_solver *)arg;
|
|
176
|
+
stc_var_id var_id = stc_var_id_from_ruby(key);
|
|
177
|
+
stc_type *type = stc_type_from_ruby(solver, value);
|
|
178
|
+
stc_bind(solver, var_id, type);
|
|
179
|
+
return ST_CONTINUE;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static void stc_load_bindings(stc_solver *solver, VALUE bindings) {
|
|
183
|
+
rb_hash_foreach(bindings, stc_load_binding_iter, (VALUE)solver);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static VALUE stc_export_bindings(stc_solver *solver) {
|
|
187
|
+
VALUE bindings = rb_hash_new();
|
|
188
|
+
for (size_t i = 0; i < solver->sub_count; i++) {
|
|
189
|
+
stc_substitution *sub = &solver->subs[i];
|
|
190
|
+
stc_type *resolved = stc_dereference(solver, sub->type);
|
|
191
|
+
VALUE key = stc_var_id_to_ruby(sub->var_id);
|
|
192
|
+
rb_hash_aset(bindings, key, stc_type_to_ruby(resolved));
|
|
193
|
+
}
|
|
194
|
+
return bindings;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
static VALUE stc_solve_graph(VALUE self, VALUE graph) {
|
|
198
|
+
(void)self;
|
|
199
|
+
Check_Type(graph, T_HASH);
|
|
200
|
+
|
|
201
|
+
stc_solver *solver = stc_solver_new();
|
|
202
|
+
if (!solver) rb_raise(rb_eNoMemError, "stc_solver_new failed");
|
|
203
|
+
|
|
204
|
+
VALUE capabilities = stc_hash_get(graph, "capabilities");
|
|
205
|
+
VALUE bindings = stc_hash_get(graph, "bindings");
|
|
206
|
+
Check_Type(capabilities, T_ARRAY);
|
|
207
|
+
|
|
208
|
+
size_t cap_count = 0;
|
|
209
|
+
stc_capability *caps = stc_load_capabilities(solver, capabilities, &cap_count);
|
|
210
|
+
if (!caps) {
|
|
211
|
+
stc_solver_free(solver);
|
|
212
|
+
rb_raise(rb_eNoMemError, "capability allocation failed");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
stc_solver_load_flat(solver, caps, cap_count);
|
|
216
|
+
if (!NIL_P(bindings)) stc_load_bindings(solver, bindings);
|
|
217
|
+
|
|
218
|
+
stc_solver_run(solver);
|
|
219
|
+
|
|
220
|
+
VALUE result = rb_hash_new();
|
|
221
|
+
rb_hash_aset(result, ID2SYM(rb_intern("bindings")), stc_export_bindings(solver));
|
|
222
|
+
rb_hash_aset(result, ID2SYM(rb_intern("iterations")), INT2NUM(solver->iterations));
|
|
223
|
+
rb_hash_aset(result, ID2SYM(rb_intern("converged")), solver->converged ? Qtrue : Qfalse);
|
|
224
|
+
|
|
225
|
+
stc_solver_free(solver);
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static void stc_solver_mark(void *ptr) {
|
|
230
|
+
(void)ptr;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static void stc_solver_free_rb(void *ptr) {
|
|
234
|
+
stc_solver_free((stc_solver *)ptr);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static size_t stc_solver_memsize(const void *ptr) {
|
|
238
|
+
(void)ptr;
|
|
239
|
+
return sizeof(stc_solver);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static const rb_data_type_t stc_solver_type = {
|
|
243
|
+
.wrap_struct_name = "Autotype::Native::Solver",
|
|
244
|
+
.function = {
|
|
245
|
+
.dmark = stc_solver_mark,
|
|
246
|
+
.dfree = stc_solver_free_rb,
|
|
247
|
+
.dsize = stc_solver_memsize,
|
|
248
|
+
},
|
|
249
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
static VALUE stc_solver_alloc(VALUE klass) {
|
|
253
|
+
stc_solver *solver = stc_solver_new();
|
|
254
|
+
if (!solver) rb_raise(rb_eNoMemError, "stc_solver_new failed");
|
|
255
|
+
return TypedData_Wrap_Struct(klass, &stc_solver_type, solver);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static VALUE stc_solver_run_rb(VALUE self) {
|
|
259
|
+
stc_solver *solver;
|
|
260
|
+
TypedData_Get_Struct(self, stc_solver, &stc_solver_type, solver);
|
|
261
|
+
int converged = stc_solver_run(solver);
|
|
262
|
+
return INT2NUM(converged ? solver->iterations : -1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static VALUE stc_solver_available(VALUE self) {
|
|
266
|
+
(void)self;
|
|
267
|
+
return Qtrue;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
void Init_autotype(void) {
|
|
271
|
+
stc_mAutotypeNative = rb_define_module("AutotypeNative");
|
|
272
|
+
stc_cSolver = rb_define_class_under(stc_mAutotypeNative, "Solver", rb_cObject);
|
|
273
|
+
rb_define_alloc_func(stc_cSolver, stc_solver_alloc);
|
|
274
|
+
rb_define_singleton_method(stc_mAutotypeNative, "available?", stc_solver_available, 0);
|
|
275
|
+
rb_define_singleton_method(stc_mAutotypeNative, "solve_graph", stc_solve_graph, 1);
|
|
276
|
+
rb_define_method(stc_cSolver, "run", stc_solver_run_rb, 0);
|
|
277
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "mkmf"
|
|
4
|
+
|
|
5
|
+
native_root = File.expand_path("../../native/autotype", __dir__)
|
|
6
|
+
native_src = File.join(native_root, "src")
|
|
7
|
+
native_include = File.join(native_root, "include")
|
|
8
|
+
|
|
9
|
+
$CFLAGS << " -O3 -std=c11 -Wall -Wextra -DNDEBUG"
|
|
10
|
+
$CFLAGS << " -I#{native_include}"
|
|
11
|
+
|
|
12
|
+
$VPATH << native_src
|
|
13
|
+
$srcs = %w[type.c solver.c autotype.c]
|
|
14
|
+
|
|
15
|
+
create_makefile("autotype")
|