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
|
# File 'lib/ai2web/server.rb', line 40
def handle(opts, method, path, body = nil, origin = nil)
manifest = Util.deep_stringify(opt(opts, :manifest))
modules = opt(opts, :modules) || {}
actions = opt(opts, :actions) || {}
validate_input = opts.key?(:validate_input) || opts.key?("validate_input") ? opt(opts, :validate_input) : true
declared_actions = {}
(manifest["actions"] || []).each { |a| declared_actions[a["name"]] = a if a.is_a?(Hash) }
trimmed = path.to_s.gsub(%r{\A/+|/+\z}, "")
path = trimmed.empty? ? "/" : "/#{trimmed}"
method = method.to_s.upcase
return { status: 204, headers: CORS.dup, body: nil } if method == "OPTIONS"
if path == "/.well-known/ai2w"
return json(200, { "ai2w" => "#{Util.trim_url(origin)}/ai2w" }) if origin && !origin.to_s.empty?
return json(200, manifest)
end
if ["/ai2w", "/ai", "/.ai"].include?(path)
return error(405, "invalid_request", "Use GET for the manifest.") if method != "GET"
return json(200, manifest)
end
if path == "/llms.txt"
return error(405, "invalid_request", "Use GET for llms.txt.") if method != "GET"
return text(200, "text/plain; charset=utf-8", Ai2Web.to_llms_txt(manifest))
end
if ["/.well-known/agent.json", "/agent.json"].include?(path)
return error(405, "invalid_request", "Use GET for agent.json.") if method != "GET"
return json(200, Ai2Web.to_agent_json(manifest))
end
if path == "/ai2w/negotiate"
b = body.is_a?(Hash) ? Util.deep_stringify(body) : {}
agent = b["agent"].is_a?(Hash) ? b["agent"] : {}
supports = agent["supports"] || b["supports"] || b
supports = {} unless supports.is_a?(Hash)
return json(200, Ai2Web.negotiate(manifest, supports))
end
if (m = ACTION_RE.match(path))
name = m[1].tr("-", "_")
fn = lookup(actions, name)
return error(404, "unsupported_capability", "Unknown action '#{name}'.") unless fn
declared = declared_actions[name]
if Util.truthy?(validate_input) && declared && declared["input_schema"]
result = Ai2Web.validate_schema(body.nil? ? {} : body, declared["input_schema"])
unless result.valid
return error(400, "invalid_request",
"Request does not match the declared input schema: #{result.errors.join("; ")}.")
end
end
return json(200, fn.call(body))
end
if (m = MODULE_RE.match(path))
name = m[1]
fn = lookup(modules, name)
return error(404, "unsupported_capability", "Module '#{name}' not exposed.") unless fn
return json(200, fn.call(body))
end
error(404, "invalid_request", "No AI2Web route for #{path}.")
end
|