From 312cc229d8cb95778a5bc4537d14626ca022cd2c Mon Sep 17 00:00:00 2001 From: Alice Date: Sat, 8 Feb 2025 02:23:22 +0300 Subject: [PATCH 01/41] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D1=8B=20?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B?= =?UTF-8?q?=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/dogsitters-finder/index.js | 57 ++++++++++++++----- .../json/2fa/dogsitter.success.json | 3 + .../dogsitters-finder/json/2fa/error.json | 3 +- .../json/2fa/owner.success.json | 3 + .../dogsitters-finder/json/2fa/success.json | 4 -- .../json/auth/dogsitter.success.json | 12 ---- .../dogsitters-finder/json/auth/error.json | 6 +- .../json/auth/owner.success.json | 9 --- .../dogsitters-finder/json/auth/success.json | 5 ++ .../json/register/dogsitter.success.json | 11 +--- .../json/register/error.json | 4 +- .../json/register/owner.success.json | 8 +-- .../json/role/dogsitter.success.json | 4 ++ .../dogsitters-finder/json/role/error.json | 5 ++ .../json/role/owner.success.json | 4 ++ 15 files changed, 79 insertions(+), 59 deletions(-) create mode 100644 server/routers/dogsitters-finder/json/2fa/dogsitter.success.json create mode 100644 server/routers/dogsitters-finder/json/2fa/owner.success.json delete mode 100644 server/routers/dogsitters-finder/json/2fa/success.json delete mode 100644 server/routers/dogsitters-finder/json/auth/dogsitter.success.json delete mode 100644 server/routers/dogsitters-finder/json/auth/owner.success.json create mode 100644 server/routers/dogsitters-finder/json/auth/success.json create mode 100644 server/routers/dogsitters-finder/json/role/dogsitter.success.json create mode 100644 server/routers/dogsitters-finder/json/role/error.json create mode 100644 server/routers/dogsitters-finder/json/role/owner.success.json diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index b34e6af..07675a4 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -7,33 +7,64 @@ router.get("/users", (request, response) => { router.post("/auth", (request, response) => { const { phoneNumber, password } = request.body; console.log(phoneNumber, password); - if (phoneNumber === "89999999999") { - response.send(require("./json/auth/dogsitter.success.json")); - } else if (phoneNumber === "89555555555") { - response.status(400).send(require("./json/auth/error.json")); + if (phoneNumber === "89999999999" || phoneNumber === "89559999999") { + response.send(require("../json/auth/success.json")); } else { - response.send(require("./json/auth/owner.success.json")); + response.status(401).send(require("../json/auth/error.json")); } }); router.post("/auth/2fa", (request, response) => { - const { code } = request.body; - if (code === "0000") { - response.send(require("./json/2fa/success.json")); + const { phoneNumber, code } = request.body; + if (code === "0000" && phoneNumber === "89999999999") { + response.send(require("../json/2fa/dogsitter.success.json")); + } else if (code === "0000" && phoneNumber === "89559999999") { + response.send(require("../json/2fa/owner.success.json")); } else { - response.status(400).send(require("./json/2fa/error.json")); + response.status(401).send(require("../json/2fa/error.json")); } }); router.post("/register", (request, response) => { const { firstName, secondName, phoneNumber, password, role } = request.body; console.log(phoneNumber, password, role); - if (phoneNumber === "89283244141" || phoneNumber === "89872855893") { - response.status(400).send(require("./json/register/error.json")); + if (phoneNumber === "89999999999" || phoneNumber === "89559999999") { + response.status(401).send(require("../json/register/error.json")); } else if (role === "dogsitter") { - response.send(require("./json/register/dogsitter.success.json")); + response.send(require("../json/register/dogsitter.success.json")); } else { - response.send(require("./json/register/owner.success.json")); + response.send(require("../json/register/owner.success.json")); + } +}); + +router.get("/auth/session", (request, response) => { + const authHeader = request.headers.authorization; + + if (!authHeader) { + return response.status(401).json({ error: "Authorization header missing" }); + } + + // Берём сам токен из заголовка + const token = authHeader.split(" ")[1]; + + if (!token) { + return response.status(401).json({ error: "Bearer token missing" }); + } + + const jwt = require("jsonwebtoken"); + const secretKey = "secret"; + + try { + const decoded = jwt.verify(token, secretKey); + + if (decoded.role === "dogsitter") { + response.send(require("../json/role/dogsitter.success.json")); + } else { + response.send(require("../json/role/owner.success.json")); + } + } catch (e) { + console.log("token e:", e); + return response.status(403).json({ error: "Invalid token" }); } }); diff --git a/server/routers/dogsitters-finder/json/2fa/dogsitter.success.json b/server/routers/dogsitters-finder/json/2fa/dogsitter.success.json new file mode 100644 index 0000000..abce97e --- /dev/null +++ b/server/routers/dogsitters-finder/json/2fa/dogsitter.success.json @@ -0,0 +1,3 @@ +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6ImRvZ3NpdHRlciIsImlhdCI6MTUxNjIzOTAyMn0.7q66wTNyLZp3TGFYF_JdU-yhlWViJulTxP_PCQzO4OI" +} diff --git a/server/routers/dogsitters-finder/json/2fa/error.json b/server/routers/dogsitters-finder/json/2fa/error.json index 7e4581e..bc20d88 100644 --- a/server/routers/dogsitters-finder/json/2fa/error.json +++ b/server/routers/dogsitters-finder/json/2fa/error.json @@ -1,4 +1,5 @@ { "status": "error", - "message": "Invalid code." + "message": "Invalid code", + "statusCode": 401 } diff --git a/server/routers/dogsitters-finder/json/2fa/owner.success.json b/server/routers/dogsitters-finder/json/2fa/owner.success.json new file mode 100644 index 0000000..545112d --- /dev/null +++ b/server/routers/dogsitters-finder/json/2fa/owner.success.json @@ -0,0 +1,3 @@ +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Mywicm9sZSI6Im93bmVyIiwiaWF0IjoxNTE2MjM5MDIyfQ.sI9839YXveTpEWhdpr5QbCYllt6hHYO7NsrQDcrXZIQ" +} \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/2fa/success.json b/server/routers/dogsitters-finder/json/2fa/success.json deleted file mode 100644 index 21e7111..0000000 --- a/server/routers/dogsitters-finder/json/2fa/success.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "status": "success", - "message": "Two-factor authentication passed." -} diff --git a/server/routers/dogsitters-finder/json/auth/dogsitter.success.json b/server/routers/dogsitters-finder/json/auth/dogsitter.success.json deleted file mode 100644 index f7b2168..0000000 --- a/server/routers/dogsitters-finder/json/auth/dogsitter.success.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "data": { - "id": 1, - "phoneNumber": 89283244141, - "firstName": "Вася", - "secondName": "Пупкин", - "role": "dogsitter", - "location": "Россия, республика Татарстан, Казань, улица Пушкина, 12", - "price": 1500, - "aboutMe": "Я люблю собак" - } -} \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/auth/error.json b/server/routers/dogsitters-finder/json/auth/error.json index 4fded6c..a3fcce3 100644 --- a/server/routers/dogsitters-finder/json/auth/error.json +++ b/server/routers/dogsitters-finder/json/auth/error.json @@ -1,3 +1,5 @@ { - "error": "Пользователь не найден" -} \ No newline at end of file + "message": "Неверный логин или пароль", + "error": "Unauthorized", + "statusCode": 401 +} diff --git a/server/routers/dogsitters-finder/json/auth/owner.success.json b/server/routers/dogsitters-finder/json/auth/owner.success.json deleted file mode 100644 index 8939302..0000000 --- a/server/routers/dogsitters-finder/json/auth/owner.success.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "data": { - "id": 3, - "phoneNumber": 89872855893, - "firstName": "Гадий", - "secondName": "Петрович", - "role": "owner" - } -} \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/auth/success.json b/server/routers/dogsitters-finder/json/auth/success.json new file mode 100644 index 0000000..ada9161 --- /dev/null +++ b/server/routers/dogsitters-finder/json/auth/success.json @@ -0,0 +1,5 @@ +{ + "status": "success", + "message": "Первый фактор аутентификации пройден", + "statusCode": 200 +} diff --git a/server/routers/dogsitters-finder/json/register/dogsitter.success.json b/server/routers/dogsitters-finder/json/register/dogsitter.success.json index 1f594e6..4327133 100644 --- a/server/routers/dogsitters-finder/json/register/dogsitter.success.json +++ b/server/routers/dogsitters-finder/json/register/dogsitter.success.json @@ -1,12 +1,3 @@ { - "data": { - "id": 5, - "phoneNumber": 89555555555, - "firstName": "Масяня", - "secondName": "Карлова", - "role": "dogsitter", - "location": "Россия, республика Татарстан, Казань, улица Пушкина, 12", - "price": 100, - "aboutMe": "Все на свете - собаки" - } + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwicm9sZSI6ImRvZ3NpdHRlciIsImlhdCI6MTUxNjIzOTAyMn0.T9V3-f3rD1deA5a2J-tYNw0cACEpzKHbhMPkc7gh8c0" } \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/register/error.json b/server/routers/dogsitters-finder/json/register/error.json index 2aaf5c9..ba91192 100644 --- a/server/routers/dogsitters-finder/json/register/error.json +++ b/server/routers/dogsitters-finder/json/register/error.json @@ -1,3 +1,5 @@ { - "error": "Пользователь с таким номером телефона уже существует" + "message": "Такой пользователь уже был зарегистрирован", + "error": "Unauthorized", + "statusCode": 401 } \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/register/owner.success.json b/server/routers/dogsitters-finder/json/register/owner.success.json index 2193e4d..3012289 100644 --- a/server/routers/dogsitters-finder/json/register/owner.success.json +++ b/server/routers/dogsitters-finder/json/register/owner.success.json @@ -1,9 +1,3 @@ { - "data": { - "id": 6, - "phoneNumber": 89888888888, - "firstName": "Генадий", - "secondName": "Паровозов", - "role": "owner" - } + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Niwicm9sZSI6Im93bmVyIiwiaWF0IjoxNTE2MjM5MDIyfQ.qgOhk9tNcaMRbarRWISTgvGx5Eq_X8fcA5lhdVs2tQI" } \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/role/dogsitter.success.json b/server/routers/dogsitters-finder/json/role/dogsitter.success.json new file mode 100644 index 0000000..2922ca7 --- /dev/null +++ b/server/routers/dogsitters-finder/json/role/dogsitter.success.json @@ -0,0 +1,4 @@ +{ + "id": 1, + "role": "dogsitter" +} \ No newline at end of file diff --git a/server/routers/dogsitters-finder/json/role/error.json b/server/routers/dogsitters-finder/json/role/error.json new file mode 100644 index 0000000..a9e44ab --- /dev/null +++ b/server/routers/dogsitters-finder/json/role/error.json @@ -0,0 +1,5 @@ +{ + "message": "Неверный jwt token", + "error": "Forbidden", + "statusCode": 403 +} diff --git a/server/routers/dogsitters-finder/json/role/owner.success.json b/server/routers/dogsitters-finder/json/role/owner.success.json new file mode 100644 index 0000000..5f2f19c --- /dev/null +++ b/server/routers/dogsitters-finder/json/role/owner.success.json @@ -0,0 +1,4 @@ +{ + "id": 3, + "role": "owner" +} \ No newline at end of file From 4d585002d7a1a227e720f228940f18706219a3ed Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 04:38:22 +0300 Subject: [PATCH 02/41] Add backend and DB settings --- server/routers/dogsitters-finder/auth.js | 74 +++++++++ server/routers/dogsitters-finder/const.js | 3 + server/routers/dogsitters-finder/index.js | 82 ++++++---- .../{ => legacy}/json/2fa/error.json | 0 .../{ => legacy}/json/2fa/success.json | 0 .../json/auth/dogsitter.success.json | 0 .../{ => legacy}/json/auth/error.json | 0 .../{ => legacy}/json/auth/owner.success.json | 0 .../json/register/dogsitter.success.json | 0 .../{ => legacy}/json/register/error.json | 0 .../json/register/owner.success.json | 0 .../{ => legacy}/json/users/users.json | 0 .../routers/dogsitters-finder/model/auth.js | 44 ++++++ .../dogsitters-finder/model/interaction.js | 24 +++ .../routers/dogsitters-finder/model/user.js | 83 ++++++++++ server/routers/dogsitters-finder/routes.js | 149 ++++++++++++++++++ 16 files changed, 424 insertions(+), 35 deletions(-) create mode 100644 server/routers/dogsitters-finder/auth.js create mode 100644 server/routers/dogsitters-finder/const.js rename server/routers/dogsitters-finder/{ => legacy}/json/2fa/error.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/2fa/success.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/auth/dogsitter.success.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/auth/error.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/auth/owner.success.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/register/dogsitter.success.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/register/error.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/register/owner.success.json (100%) rename server/routers/dogsitters-finder/{ => legacy}/json/users/users.json (100%) create mode 100644 server/routers/dogsitters-finder/model/auth.js create mode 100644 server/routers/dogsitters-finder/model/interaction.js create mode 100644 server/routers/dogsitters-finder/model/user.js create mode 100644 server/routers/dogsitters-finder/routes.js diff --git a/server/routers/dogsitters-finder/auth.js b/server/routers/dogsitters-finder/auth.js new file mode 100644 index 0000000..f8c159d --- /dev/null +++ b/server/routers/dogsitters-finder/auth.js @@ -0,0 +1,74 @@ +const { Router } = require("express"); +const hash = require("pbkdf2-password")(); +const { promisify } = require("node:util"); +const jwt = require('jsonwebtoken') + +const { getAnswer } = require("../../utils/common"); + +const { AuthModel } = require("./model/todo/auth"); +const { TOKEN_KEY } = require('./const') +const { UserModel } = require("./model/todo/user"); + +const { requiredValidate } = require('./utils') + +const router = Router(); + +router.post( + "/signup", + requiredValidate("login", "password", "email"), + async (req, res, next) => { + const { login, password, email } = req.body + + const user = await AuthModel.findOne({ login }); + + if (user) { + throw new Error("Пользователь с таким логином уже существует"); + } + + hash({ password }, async function (err, pass, salt, hash) { + if (err) return next(err); + + const user = await UserModel.create({ login, email }); + await AuthModel.create({ login, hash, salt, userId: user.id }); + + res.json(getAnswer(null, { ok: true })) + }) + } +) + +function authenticate(login, pass, cb) { + AuthModel.findOne({ login }).populate('userId').exec().then((user) => { + if (!user) return cb(null, null) + + hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) { + if (err) return cb(err) + if (hash === user.hash) return cb(null, user) + cb(null, null) + }) + }) +} + +const auth = promisify(authenticate) + +router.post('/signin', requiredValidate('login', 'password'), async (req, res) => { + const { login, password } = req.body + + const user = await auth(login, password) + + if (!user) { + throw new Error("Неверный логин или пароль") + } + + const accessToken = jwt.sign({ + ...JSON.parse(JSON.stringify(user.userId)), + }, TOKEN_KEY, { + expiresIn: '12h' + }) + + res.json(getAnswer(null, { + user: user.userId, + token: accessToken, + })) +}) + +module.exports = router diff --git a/server/routers/dogsitters-finder/const.js b/server/routers/dogsitters-finder/const.js new file mode 100644 index 0000000..7828a91 --- /dev/null +++ b/server/routers/dogsitters-finder/const.js @@ -0,0 +1,3 @@ +exports.DSF_AUTH_PASSWD_MODEL_NAME = 'DSF_AUTH_PASSWD' +exports.DSF_AUTH_USER_MODEL_NAME = 'DSF_AUTH_USER' +exports.DSF_INTERACTION_MODEL_NAME = 'DSF_INTERACTION' diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index b34e6af..a618a12 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -1,40 +1,52 @@ -const router = require("express").Router(); +// const router = require("express").Router(); -router.get("/users", (request, response) => { - response.send(require("./json/users/users.json")); -}); +// router.get("/users", (request, response) => { +// response.send(require("./json/users/users.json")); +// }); -router.post("/auth", (request, response) => { - const { phoneNumber, password } = request.body; - console.log(phoneNumber, password); - if (phoneNumber === "89999999999") { - response.send(require("./json/auth/dogsitter.success.json")); - } else if (phoneNumber === "89555555555") { - response.status(400).send(require("./json/auth/error.json")); - } else { - response.send(require("./json/auth/owner.success.json")); - } -}); +// router.post("/auth", (request, response) => { +// const { phoneNumber, password } = request.body; +// console.log(phoneNumber, password); +// if (phoneNumber === "89999999999") { +// response.send(require("./json/auth/dogsitter.success.json")); +// } else if (phoneNumber === "89555555555") { +// response.status(400).send(require("./json/auth/error.json")); +// } else { +// response.send(require("./json/auth/owner.success.json")); +// } +// }); -router.post("/auth/2fa", (request, response) => { - const { code } = request.body; - if (code === "0000") { - response.send(require("./json/2fa/success.json")); - } else { - response.status(400).send(require("./json/2fa/error.json")); - } -}); +// router.post("/auth/2fa", (request, response) => { +// const { code } = request.body; +// if (code === "0000") { +// response.send(require("./json/2fa/success.json")); +// } else { +// response.status(400).send(require("./json/2fa/error.json")); +// } +// }); -router.post("/register", (request, response) => { - const { firstName, secondName, phoneNumber, password, role } = request.body; - console.log(phoneNumber, password, role); - if (phoneNumber === "89283244141" || phoneNumber === "89872855893") { - response.status(400).send(require("./json/register/error.json")); - } else if (role === "dogsitter") { - response.send(require("./json/register/dogsitter.success.json")); - } else { - response.send(require("./json/register/owner.success.json")); - } -}); +// router.post("/register", (request, response) => { +// const { firstName, secondName, phoneNumber, password, role } = request.body; +// console.log(phoneNumber, password, role); +// if (phoneNumber === "89283244141" || phoneNumber === "89872855893") { +// response.status(400).send(require("./json/register/error.json")); +// } else if (role === "dogsitter") { +// response.send(require("./json/register/dogsitter.success.json")); +// } else { +// response.send(require("./json/register/owner.success.json")); +// } +// }); -module.exports = router; +// module.exports = router; + +const { Router } = require('express') + +const router = Router() + +const userInteractionRouter = require('./routes') +const authRouter = require('./auth') + +router.use('/auth', authRouter) +router.use('/user-interaction', userInteractionRouter) + +module.exports = router diff --git a/server/routers/dogsitters-finder/json/2fa/error.json b/server/routers/dogsitters-finder/legacy/json/2fa/error.json similarity index 100% rename from server/routers/dogsitters-finder/json/2fa/error.json rename to server/routers/dogsitters-finder/legacy/json/2fa/error.json diff --git a/server/routers/dogsitters-finder/json/2fa/success.json b/server/routers/dogsitters-finder/legacy/json/2fa/success.json similarity index 100% rename from server/routers/dogsitters-finder/json/2fa/success.json rename to server/routers/dogsitters-finder/legacy/json/2fa/success.json diff --git a/server/routers/dogsitters-finder/json/auth/dogsitter.success.json b/server/routers/dogsitters-finder/legacy/json/auth/dogsitter.success.json similarity index 100% rename from server/routers/dogsitters-finder/json/auth/dogsitter.success.json rename to server/routers/dogsitters-finder/legacy/json/auth/dogsitter.success.json diff --git a/server/routers/dogsitters-finder/json/auth/error.json b/server/routers/dogsitters-finder/legacy/json/auth/error.json similarity index 100% rename from server/routers/dogsitters-finder/json/auth/error.json rename to server/routers/dogsitters-finder/legacy/json/auth/error.json diff --git a/server/routers/dogsitters-finder/json/auth/owner.success.json b/server/routers/dogsitters-finder/legacy/json/auth/owner.success.json similarity index 100% rename from server/routers/dogsitters-finder/json/auth/owner.success.json rename to server/routers/dogsitters-finder/legacy/json/auth/owner.success.json diff --git a/server/routers/dogsitters-finder/json/register/dogsitter.success.json b/server/routers/dogsitters-finder/legacy/json/register/dogsitter.success.json similarity index 100% rename from server/routers/dogsitters-finder/json/register/dogsitter.success.json rename to server/routers/dogsitters-finder/legacy/json/register/dogsitter.success.json diff --git a/server/routers/dogsitters-finder/json/register/error.json b/server/routers/dogsitters-finder/legacy/json/register/error.json similarity index 100% rename from server/routers/dogsitters-finder/json/register/error.json rename to server/routers/dogsitters-finder/legacy/json/register/error.json diff --git a/server/routers/dogsitters-finder/json/register/owner.success.json b/server/routers/dogsitters-finder/legacy/json/register/owner.success.json similarity index 100% rename from server/routers/dogsitters-finder/json/register/owner.success.json rename to server/routers/dogsitters-finder/legacy/json/register/owner.success.json diff --git a/server/routers/dogsitters-finder/json/users/users.json b/server/routers/dogsitters-finder/legacy/json/users/users.json similarity index 100% rename from server/routers/dogsitters-finder/json/users/users.json rename to server/routers/dogsitters-finder/legacy/json/users/users.json diff --git a/server/routers/dogsitters-finder/model/auth.js b/server/routers/dogsitters-finder/model/auth.js new file mode 100644 index 0000000..40a8a97 --- /dev/null +++ b/server/routers/dogsitters-finder/model/auth.js @@ -0,0 +1,44 @@ +const { Schema, model } = require("mongoose"); + +const { + DSF_AUTH_PASSWD_MODEL_NAME, + DSF_AUTH_USER_MODEL_NAME, +} = require("../../const"); + +const schema = new Schema({ + login: { + type: String, + required: true, + unique: true + }, + hash: { + type: String, + required: true + }, + salt: { + type: String, + required: true + }, + userId: { + type: Schema.Types.ObjectId, + ref: DSF_AUTH_USER_MODEL_NAME + }, + created: { + type: Date, + default: () => new Date().toISOString(), + }, +}); + +schema.set("toJSON", { + virtuals: true, + versionKey: false, + transform: function (doc, ret) { + delete ret._id; + }, +}); + +schema.virtual("id").get(function () { + return this._id.toHexString(); +}); + +exports.AuthModel = model(DSF_AUTH_PASSWD_MODEL_NAME, schema); diff --git a/server/routers/dogsitters-finder/model/interaction.js b/server/routers/dogsitters-finder/model/interaction.js new file mode 100644 index 0000000..95f202b --- /dev/null +++ b/server/routers/dogsitters-finder/model/interaction.js @@ -0,0 +1,24 @@ +const { Schema, model } = require("mongoose"); + +const { DSF_AUTH_USER_MODEL_NAME, DSF_INTERACTION_MODEL_NAME } = require("../../const"); + +const interactionSchema = new Schema({ + owner_id: { + type: Schema.Types.ObjectId, + ref: DSF_AUTH_USER_MODEL_NAME, + required: true + }, + dogsitter_id: { + type: Schema.Types.ObjectId, + ref: DSF_AUTH_USER_MODEL_NAME, + required: true + }, + timestamp: { + type: Date, + default: Date.now + } +}); + +interactionSchema.index({ owner_id: 1, dogsitter_id: 1 }); + +module.exports.Interaction = model(DSF_INTERACTION_MODEL_NAME, interactionSchema); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/model/user.js b/server/routers/dogsitters-finder/model/user.js new file mode 100644 index 0000000..889c946 --- /dev/null +++ b/server/routers/dogsitters-finder/model/user.js @@ -0,0 +1,83 @@ +const { Schema, model } = require("mongoose"); + +const { DSF_AUTH_USER_MODEL_NAME } = require("../../const"); + +const userSchema = new Schema({ + phone_number: { + type: String, + required: true, + unique: true, + match: /^\+?\d{10,15}$/ + }, + first_name: { + type: String, + required: true, + trim: true + }, + second_name: { + type: String, + required: true, + trim: true + }, + role: { + type: String, + enum: ["dogsitter", "owner"], + required: true + }, + location: { + type: String, + required: function() { + return this.role === "dogsitter"; + } + }, + price: { + type: Number, + min: 0, + required: function() { + return this.role === "dogsitter"; + } + }, + about_me: { + type: String, + maxlength: 500 + }, + rating: { + type: Number, + min: 0, + max: 5, + default: 0 + }, + ratings: { + type: [Number], + default: [], + validate: { + validator: function(arr) { + return arr.every(v => v >= 0 && v <= 5); + }, + message: "Рейтинг должен быть в диапазоне от 0 до 5!" + } + }, + tg: { + type: String, + match: /^[a-zA-Z0-9_]{5,32}$/ + }, + created: { + type: Date, + default: Date.now + } +}); + +userSchema.virtual("id").get(function() { + return this._id.toHexString(); +}); + +userSchema.set("toJSON", { + virtuals: true, + versionKey: false, + transform: function(doc, ret) { + delete ret._id; + delete ret.__v; + } +}); + +module.exports.User = model(DSF_AUTH_USER_MODEL_NAME, userSchema); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/routes.js b/server/routers/dogsitters-finder/routes.js new file mode 100644 index 0000000..f82bc47 --- /dev/null +++ b/server/routers/dogsitters-finder/routes.js @@ -0,0 +1,149 @@ +const { Router } = require('express') +const { expressjwt } = require('express-jwt') + +const { getAnswer } = require('../../utils/common') +const { User, Interaction } = require('./model') +const { TOKEN_KEY } = require('./const') +const { requiredValidate } = require('./utils') + +const router = Router() + +// Получение списка пользователей +router.get('/users', async (req, res) => { + + const users = await User.find() + .select('-__v -ratings -phone_number') + .lean() + + console.log('get users successfull') + + res.send(getAnswer(null, users)) +}) + +// Получение конкретного пользователя +router.get('/dogsitter-viewing', async (req, res) => { + const { userId } = req.params + + const user = await User.findById(userId) + .select('-__v -ratings') + .lean() + + if (!user) { + return res.status(404).send(getAnswer(new Error('Пользователь не найден'))) + } + + res.send(getAnswer(null, user)) +}) + +router.use(expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] })) + +// Добавление оценки пользователю +router.post('/dogsitter-viewing/rating', requiredValidate('value'), async (req, res) => { + const { userId } = req.params + const { value } = req.body + const authUserId = req.auth.id + + try { + const user = await User.findById(userId) + if (!user) throw new Error('Пользователь не найден') + if (user.role !== 'dogsitter') throw new Error('Нельзя оценивать этого пользователя') + if (user.id === authUserId) throw new Error('Нельзя оценивать самого себя') + + user.ratings.push(Number(value)) + user.rating = user.ratings.reduce((a, b) => a + b, 0) / user.ratings.length + + const updatedUser = await user.save() + + res.send(getAnswer(null, { + id: updatedUser.id, + rating: updatedUser.rating.toFixed(1), + totalRatings: updatedUser.ratings.length + })) + + } catch (error) { + res.status(400).send(getAnswer(error)) + } +}) + +// Обновление информации пользователя +router.patch('/users', async (req, res) => { + const { userId } = req.params + const updates = req.body + + try { + const user = await User.findByIdAndUpdate(userId, updates, { new: true }) + .select('-__v -ratings') + + if (!user) throw new Error('Пользователь не найден') + res.send(getAnswer(null, user)) + + } catch (error) { + res.status(400).send(getAnswer(error)) + } +}) + +// Создание объекта взаимодействия +router.post('/interactions', + expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }), + requiredValidate('dogsitter_id'), + async (req, res) => { + try { + const { dogsitter_id } = req.body + const owner_id = req.auth.id // ID из JWT токена + + // Проверка существования пользователей + const [owner, dogsitter] = await Promise.all([ + User.findById(owner_id), + User.findById(dogsitter_id) + ]) + + if (!owner || owner.role !== 'owner') { + throw new Error('Владелец не найден или имеет неверную роль') + } + + if (!dogsitter || dogsitter.role !== 'dogsitter') { + throw new Error('Догситтер не найден или имеет неверную роль') + } + + // Создание взаимодействия + const interaction = await Interaction.create({ + owner_id, + dogsitter_id + }) + + res.send(getAnswer(null, { + id: interaction.id, + timestamp: interaction.timestamp + })) + + } catch (error) { + res.status(400).send(getAnswer(error)) + } + } +) + +router.get('/interactions/check', async (req, res) => { + const { owner_id, dogsitter_id } = req.query; + + if (!owner_id || !dogsitter_id) { + return res.status(400).send(getAnswer('Missing owner_id or dogsitter_id')); + } + + try { + // Поиск взаимодействий по owner_id и dogsitter_id + const interactions = await Interaction.find({ owner_id, dogsitter_id }) + .select('-__v') // Выбираем только нужные поля + .lean(); + + if (interactions.length === 0) { + return res.status(404).send(getAnswer('No interactions found')); + } + + res.send(getAnswer(null, interactions)); + } catch (error) { + console.error('Error checking interactions:', error); + res.status(500).send(getAnswer('Internal Server Error')); + } +}); + +module.exports = router \ No newline at end of file From 5e4a99529dd6d378179e2339bf691fc48e2190f6 Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 04:44:17 +0300 Subject: [PATCH 03/41] Add backend and db settings --- server/routers/dogsitters-finder/index.js | 82 ++++++++----------- .../{legacy => }/json/2fa/error.json | 0 .../{legacy => }/json/2fa/success.json | 0 .../json/auth/dogsitter.success.json | 0 .../{legacy => }/json/auth/error.json | 0 .../{legacy => }/json/auth/owner.success.json | 0 .../json/register/dogsitter.success.json | 0 .../{legacy => }/json/register/error.json | 0 .../json/register/owner.success.json | 0 .../{legacy => }/json/users/users.json | 0 10 files changed, 34 insertions(+), 48 deletions(-) rename server/routers/dogsitters-finder/{legacy => }/json/2fa/error.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/2fa/success.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/auth/dogsitter.success.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/auth/error.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/auth/owner.success.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/register/dogsitter.success.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/register/error.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/register/owner.success.json (100%) rename server/routers/dogsitters-finder/{legacy => }/json/users/users.json (100%) diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index a618a12..c0cfd87 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -1,52 +1,38 @@ -// const router = require("express").Router(); +const router = require("express").Router(); -// router.get("/users", (request, response) => { -// response.send(require("./json/users/users.json")); -// }); +router.get("/users", (request, response) => { + response.send(require("./json/users/users.json")); +}); -// router.post("/auth", (request, response) => { -// const { phoneNumber, password } = request.body; -// console.log(phoneNumber, password); -// if (phoneNumber === "89999999999") { -// response.send(require("./json/auth/dogsitter.success.json")); -// } else if (phoneNumber === "89555555555") { -// response.status(400).send(require("./json/auth/error.json")); -// } else { -// response.send(require("./json/auth/owner.success.json")); -// } -// }); +router.post("/auth", (request, response) => { + const { phoneNumber, password } = request.body; + console.log(phoneNumber, password); + if (phoneNumber === "89999999999") { + response.send(require("./json/auth/dogsitter.success.json")); + } else if (phoneNumber === "89555555555") { + response.status(400).send(require("./json/auth/error.json")); + } else { + response.send(require("./json/auth/owner.success.json")); + } +}); -// router.post("/auth/2fa", (request, response) => { -// const { code } = request.body; -// if (code === "0000") { -// response.send(require("./json/2fa/success.json")); -// } else { -// response.status(400).send(require("./json/2fa/error.json")); -// } -// }); +router.post("/auth/2fa", (request, response) => { + const { code } = request.body; + if (code === "0000") { + response.send(require("./json/2fa/success.json")); + } else { + response.status(400).send(require("./json/2fa/error.json")); + } +}); -// router.post("/register", (request, response) => { -// const { firstName, secondName, phoneNumber, password, role } = request.body; -// console.log(phoneNumber, password, role); -// if (phoneNumber === "89283244141" || phoneNumber === "89872855893") { -// response.status(400).send(require("./json/register/error.json")); -// } else if (role === "dogsitter") { -// response.send(require("./json/register/dogsitter.success.json")); -// } else { -// response.send(require("./json/register/owner.success.json")); -// } -// }); - -// module.exports = router; - -const { Router } = require('express') - -const router = Router() - -const userInteractionRouter = require('./routes') -const authRouter = require('./auth') - -router.use('/auth', authRouter) -router.use('/user-interaction', userInteractionRouter) - -module.exports = router +router.post("/register", (request, response) => { + const { firstName, secondName, phoneNumber, password, role } = request.body; + console.log(phoneNumber, password, role); + if (phoneNumber === "89283244141" || phoneNumber === "89872855893") { + response.status(400).send(require("./json/register/error.json")); + } else if (role === "dogsitter") { + response.send(require("./json/register/dogsitter.success.json")); + } else { + response.send(require("./json/register/owner.success.json")); + } +}); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/legacy/json/2fa/error.json b/server/routers/dogsitters-finder/json/2fa/error.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/2fa/error.json rename to server/routers/dogsitters-finder/json/2fa/error.json diff --git a/server/routers/dogsitters-finder/legacy/json/2fa/success.json b/server/routers/dogsitters-finder/json/2fa/success.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/2fa/success.json rename to server/routers/dogsitters-finder/json/2fa/success.json diff --git a/server/routers/dogsitters-finder/legacy/json/auth/dogsitter.success.json b/server/routers/dogsitters-finder/json/auth/dogsitter.success.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/auth/dogsitter.success.json rename to server/routers/dogsitters-finder/json/auth/dogsitter.success.json diff --git a/server/routers/dogsitters-finder/legacy/json/auth/error.json b/server/routers/dogsitters-finder/json/auth/error.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/auth/error.json rename to server/routers/dogsitters-finder/json/auth/error.json diff --git a/server/routers/dogsitters-finder/legacy/json/auth/owner.success.json b/server/routers/dogsitters-finder/json/auth/owner.success.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/auth/owner.success.json rename to server/routers/dogsitters-finder/json/auth/owner.success.json diff --git a/server/routers/dogsitters-finder/legacy/json/register/dogsitter.success.json b/server/routers/dogsitters-finder/json/register/dogsitter.success.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/register/dogsitter.success.json rename to server/routers/dogsitters-finder/json/register/dogsitter.success.json diff --git a/server/routers/dogsitters-finder/legacy/json/register/error.json b/server/routers/dogsitters-finder/json/register/error.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/register/error.json rename to server/routers/dogsitters-finder/json/register/error.json diff --git a/server/routers/dogsitters-finder/legacy/json/register/owner.success.json b/server/routers/dogsitters-finder/json/register/owner.success.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/register/owner.success.json rename to server/routers/dogsitters-finder/json/register/owner.success.json diff --git a/server/routers/dogsitters-finder/legacy/json/users/users.json b/server/routers/dogsitters-finder/json/users/users.json similarity index 100% rename from server/routers/dogsitters-finder/legacy/json/users/users.json rename to server/routers/dogsitters-finder/json/users/users.json From f254d57db4be10efec21a4f19d06b34a0ef56456 Mon Sep 17 00:00:00 2001 From: aaeii Date: Sat, 8 Feb 2025 09:57:34 +0300 Subject: [PATCH 04/41] upd json --- server/routers/gamehub/index.js | 161 +++++++++++++++++- .../gamehub/json/gamepage/success.json | 8 +- .../gamehub/json/home-page-data/success.json | 16 +- 3 files changed, 169 insertions(+), 16 deletions(-) diff --git a/server/routers/gamehub/index.js b/server/routers/gamehub/index.js index 34af599..301a512 100644 --- a/server/routers/gamehub/index.js +++ b/server/routers/gamehub/index.js @@ -8,6 +8,10 @@ router.get("/update-like", (request, response) => { response.send(require("./json/gamepage/success.json")); }); +router.get("/add-to-cart", (request, response) => { + response.send(require("./json/home-page-data/games-in-cart.json")); +}); + router.get("/categories", (request, response) => { response.send(require("./json/home-page-data/all-games.json")); }); @@ -16,18 +20,32 @@ router.get("/favourites", (request, response) => { response.send(require("./json/home-page-data/all-games.json")); }); +// router.get("/shopping-cart", (request, response) => { +// response.send(require("../json/shopping-cart/success.json")); +// }); + router.get("/shopping-cart", (request, response) => { - response.send(require("./json/shopping-cart/success.json")); + response.send(require("./json/home-page-data/games-in-cart.json")); }); -router.get("/home", (request, response) => { - response.send(require("./json/home-page-data/success.json")); +// Добавляем поддержку разных ответов для /home +router.get("/home", (req, res) => { + if (stubs.home === "success") { + res.send(require("./json/home-page-data/success.json")); + } else if (stubs.home === "empty") { + res.send({ data: [] }); // Отправляем пустой массив + } else { + res.status(500).json({ success: false, message: "Server error" }); + } }); router.get("/all-games", (request, response) => { response.send(require("./json/home-page-data/all-games.json")); }); +const stubs = { + home: "success", +}; // // Маршрут для обновления лайков // router.post("/update-like", (request, response) => { @@ -42,18 +60,17 @@ router.get("/all-games", (request, response) => { // }); // }); - const fs = require("fs").promises; const path = require("path"); // Path to JSON file -const commentsFilePath = path.join(__dirname, "./json/gamepage/success.json"); +const commentsFilePath = path.join(__dirname, "../json/gamepage/success.json"); // Read JSON file async function readComments() { const data = await fs.readFile(commentsFilePath, "utf-8"); const parsedData = JSON.parse(data); - console.log("Прочитанные данные:", parsedData); // Логируем полученные данные + console.log("Прочитанные данные:", parsedData); // Логируем полученные данные return parsedData; } // Write to JSON file @@ -92,5 +109,137 @@ router.post("/update-like", async (req, res) => { } }); +// Путь к JSON-файлу с корзиной +const cartFilePath = path.join( + __dirname, + "../json/home-page-data/games-in-cart.json" +); + +// Функция для чтения JSON-файла +async function readCart() { + const data = await fs.readFile(cartFilePath, "utf-8"); + return JSON.parse(data); +} + +// Функция для записи в JSON-файл +async function writeCart(data) { + await fs.writeFile(cartFilePath, JSON.stringify(data, null, 2), "utf-8"); +} + +// Маршрут для добавления/удаления товара в корзине +router.post("/add-to-cart", async (req, res) => { + const { id, action } = req.body; + + // Проверка наличия id и action + if (id === undefined || action === undefined) { + return res + .status(400) + .json({ success: false, message: "Invalid id or action" }); + } + + try { + const cartData = await readCart(); + let ids = cartData.data.ids; + + if (action === "add") { + // Если action "add", добавляем товар, если его нет в корзине + if (!ids?.includes(id)) { + ids.push(id); + } + } else if (action === "remove") { + // Если action "remove", удаляем товар, если он есть в корзине + if (ids?.includes(id)) { + ids = ids.filter((item) => item !== id); + } + } else { + // Если action невалиден + return res + .status(400) + .json({ success: false, message: "Invalid action" }); + } + + // Записываем обновленные данные обратно в файл + cartData.data.ids = ids; + await writeCart(cartData); + + res.status(200).json({ + success: true, + message: "Cart updated successfully", + data: cartData.data, // Возвращаем обновленные данные + }); + } catch (error) { + console.error("Error updating cart:", error); + res.status(500).json({ success: false, message: "Server error" }); + } +}); module.exports = router; + +const createElement = (key, value, buttonTitle) => ` + +`; + +router.get("/admin/home", (request, response) => { + response.send(` +
+
+ Настройка данных для /home + ${createElement("home", "success", "Отдать успешный ответ")} + ${createElement("home", "empty", "Отдать пустой массив")} + ${createElement("home", "error", "Отдать ошибку")} +
+
+ `); +}); + + +router.get("/admin/game-page", (request, response) => { + response.send(` +
+
+ Настройка данных для /game-page + ${createElement("game-page", "success", "Отдать успешный ответ")} + ${createElement("game-page", "empty", "Отдать пустой массив")} + ${createElement("game-page", "error", "Отдать ошибку")} + +
+
+ `); +}); + +router.get("/admin/categories", (request, response) => { + response.send(` +
+
+ Настройка данных для /categories + ${createElement("categories", "success", "Отдать успешный ответ")} + ${createElement("categories", "empty", "Отдать пустой массив")} + ${createElement("categories", "error", "Отдать ошибку")} +
+
+ `); +}); + +router.get("/admin/favourites", (request, response) => { + response.send(` +
+
+ Настройка данных для /favourites + ${createElement("favourites", "success", "Отдать успешный ответ")} + ${createElement("favourites", "empty", "Отдать пустой массив")} + ${createElement("favourites", "error", "Отдать ошибку")} +
+
+ `); +}); + +router.get("/admin/set/:key/:value", (request, response) => { + const { key, value } = request.params; + stubs[key] = value; + response.send("Настройки обновлены!"); +}); \ No newline at end of file diff --git a/server/routers/gamehub/json/gamepage/success.json b/server/routers/gamehub/json/gamepage/success.json index c4b9801..958704e 100644 --- a/server/routers/gamehub/json/gamepage/success.json +++ b/server/routers/gamehub/json/gamepage/success.json @@ -5,28 +5,28 @@ { "username": "Пользователь1", "text": "Текст комментария 1", - "likes": 11, + "likes": 13, "rating": 8, "date": "2025-03-01T10:00:00Z" }, { "username": "Пользователь2", "text": "Текст комментария 2", - "likes": 7, + "likes": 10, "rating": 7, "date": "2025-01-01T10:00:00Z" }, { "username": "Пользователь3", "text": "Текст комментария 3", - "likes": 2, + "likes": 4, "rating": 3, "date": "2025-02-01T10:00:00Z" }, { "username": "Пользователь4", "text": "Текст комментария 4", - "likes": 15, + "likes": 18, "rating": 2, "date": "2025-12-01T10:00:00Z" } diff --git a/server/routers/gamehub/json/home-page-data/success.json b/server/routers/gamehub/json/home-page-data/success.json index bd83dcb..c393e06 100644 --- a/server/routers/gamehub/json/home-page-data/success.json +++ b/server/routers/gamehub/json/home-page-data/success.json @@ -105,23 +105,27 @@ { "image": "news1", "text": "Разработчики Delta Force: Hawk Ops представили крупномасштабный режим Havoc Warfare", - "imgPath": "img_news_1" + "imgPath": "img_news_1", + "link": "https://gamemag.ru/news/185583/delta-force-hawk-ops-gameplay-showcase-havoc-warfare" }, { "image": "news2", "text": "Первый трейлер Assassin’s Creed Shadows — с темнокожим самураем в феодальной Японии", - "imgPath": "img_news_2" + "imgPath": "img_news_2", + "link": "https://stopgame.ru/newsdata/62686/pervyy_trailer_assassin_s_creed_shadows_s_temnokozhim_samuraem_v_feodalnoy_yaponii" }, { "image": "news3", "text": "Призрак Цусимы» вышел на ПК — и уже ставит рекорды для Sony", - "imgPath": "img_news_3" + "imgPath": "img_news_3", + "link": "https://stopgame.ru/newsdata/62706/prizrak_cusimy_vyshel_na_pk_i_uzhe_stavit_rekordy_dlya_sony" }, { "image": "news4", - "text": "Авторы Skull and Bones расширяют планы на второй сезо", - "imgPath": "img_news_4" + "text": "Авторы Skull and Bones расширяют планы на второй сезон", + "imgPath": "img_news_4", + "link": "https://stopgame.ru/newsdata/62711/avtory_skull_and_bones_rasshiryayut_plany_na_vtoroy_sezon" } ] } -} +} \ No newline at end of file From f1a93bffb5e7cc01a24ca7f4cfb21d08f45e613c Mon Sep 17 00:00:00 2001 From: aaeii Date: Sat, 8 Feb 2025 10:33:24 +0300 Subject: [PATCH 05/41] fix path --- server/routers/gamehub/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/routers/gamehub/index.js b/server/routers/gamehub/index.js index 301a512..489588f 100644 --- a/server/routers/gamehub/index.js +++ b/server/routers/gamehub/index.js @@ -64,7 +64,7 @@ const fs = require("fs").promises; const path = require("path"); // Path to JSON file -const commentsFilePath = path.join(__dirname, "../json/gamepage/success.json"); +const commentsFilePath = path.join(__dirname, "./json/gamepage/success.json"); // Read JSON file async function readComments() { @@ -112,7 +112,7 @@ router.post("/update-like", async (req, res) => { // Путь к JSON-файлу с корзиной const cartFilePath = path.join( __dirname, - "../json/home-page-data/games-in-cart.json" + "./json/home-page-data/games-in-cart.json" ); // Функция для чтения JSON-файла From 39db7b4d26e1e1a8e1e39e1cfd71407959da2329 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Sat, 8 Feb 2025 10:39:45 +0300 Subject: [PATCH 06/41] fix --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 1e81309..3cfef74 100644 --- a/server/index.js +++ b/server/index.js @@ -81,7 +81,7 @@ app.use(require("./root")) app.use("/kfu-m-24-1", require("./routers/kfu-m-24-1")) app.use("/epja-2024-1", require("./routers/epja-2024-1")) app.use("/v1/todo", require("./routers/todo")) -app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) +// app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) app.use("/kazan-explore", require("./routers/kazan-explore")) app.use("/edateam", require("./routers/edateam-legacy")) app.use("/dry-wash", require("./routers/dry-wash")) From 72a266754979d38980399fc41ae734e7d6aaef29 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Sat, 8 Feb 2025 10:39:49 +0300 Subject: [PATCH 07/41] 1.2.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c39cfa6..9404495 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "multi-stub", - "version": "1.2.0", + "version": "1.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "multi-stub", - "version": "1.2.0", + "version": "1.2.1", "license": "MIT", "dependencies": { "ai": "^4.1.13", diff --git a/package.json b/package.json index 8ef622c..5acc7e8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "multi-stub", - "version": "1.2.0", + "version": "1.2.1", "description": "", "main": "index.js", "scripts": { From 6e37fe93f7bf336e53ea5372ffd3193487bc09d3 Mon Sep 17 00:00:00 2001 From: aaeii Date: Sat, 8 Feb 2025 11:44:00 +0300 Subject: [PATCH 08/41] =?UTF-8?q?=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B0=D0=B4=D0=BC=D0=B8=D0=BD=20=D0=BF?= =?UTF-8?q?=D0=B0=D0=BD=D0=B5=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/gamehub/index.js | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/server/routers/gamehub/index.js b/server/routers/gamehub/index.js index 489588f..1b430c0 100644 --- a/server/routers/gamehub/index.js +++ b/server/routers/gamehub/index.js @@ -21,7 +21,7 @@ router.get("/favourites", (request, response) => { }); // router.get("/shopping-cart", (request, response) => { -// response.send(require("../json/shopping-cart/success.json")); +// response.send(require("./json/shopping-cart/success.json")); // }); router.get("/shopping-cart", (request, response) => { @@ -175,35 +175,39 @@ router.post("/add-to-cart", async (req, res) => { module.exports = router; -const createElement = (key, value, buttonTitle) => ` +const createElement = (key, value, buttonTitle, basePath) => ` `; router.get("/admin/home", (request, response) => { + const basePath = request.baseUrl; // Получаем базовый путь маршрутизатора response.send(`
Настройка данных для /home - ${createElement("home", "success", "Отдать успешный ответ")} - ${createElement("home", "empty", "Отдать пустой массив")} - ${createElement("home", "error", "Отдать ошибку")} + ${createElement("home", "success", "Отдать успешный ответ", basePath)} + ${createElement("home", "empty", "Отдать пустой массив", basePath)} + ${createElement("home", "error", "Отдать ошибку", basePath)}
`); }); - router.get("/admin/game-page", (request, response) => { response.send(`
Настройка данных для /game-page - ${createElement("game-page", "success", "Отдать успешный ответ")} + ${createElement( + "game-page", + "success", + "Отдать успешный ответ" + )} ${createElement("game-page", "empty", "Отдать пустой массив")} ${createElement("game-page", "error", "Отдать ошибку")} @@ -217,7 +221,11 @@ router.get("/admin/categories", (request, response) => {
Настройка данных для /categories - ${createElement("categories", "success", "Отдать успешный ответ")} + ${createElement( + "categories", + "success", + "Отдать успешный ответ" + )} ${createElement("categories", "empty", "Отдать пустой массив")} ${createElement("categories", "error", "Отдать ошибку")}
@@ -230,7 +238,11 @@ router.get("/admin/favourites", (request, response) => {
Настройка данных для /favourites - ${createElement("favourites", "success", "Отдать успешный ответ")} + ${createElement( + "favourites", + "success", + "Отдать успешный ответ" + )} ${createElement("favourites", "empty", "Отдать пустой массив")} ${createElement("favourites", "error", "Отдать ошибку")}
From 48550416d90af152896d77c838455074c1cd44c1 Mon Sep 17 00:00:00 2001 From: Alice Date: Sat, 8 Feb 2025 12:40:39 +0300 Subject: [PATCH 09/41] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=83=D1=82=D0=B5=D0=B9=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/dogsitters-finder/index.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index aa0be5f..7e8163b 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -8,20 +8,20 @@ router.post("/auth", (request, response) => { const { phoneNumber, password } = request.body; console.log(phoneNumber, password); if (phoneNumber === "89999999999" || phoneNumber === "89559999999") { - response.send(require("../json/auth/success.json")); + response.send(require("./json/auth/success.json")); } else { - response.status(401).send(require("../json/auth/error.json")); + response.status(401).send(require("./json/auth/error.json")); } }); router.post("/auth/2fa", (request, response) => { const { phoneNumber, code } = request.body; if (code === "0000" && phoneNumber === "89999999999") { - response.send(require("../json/2fa/dogsitter.success.json")); + response.send(require("./json/2fa/dogsitter.success.json")); } else if (code === "0000" && phoneNumber === "89559999999") { - response.send(require("../json/2fa/owner.success.json")); + response.send(require("./json/2fa/owner.success.json")); } else { - response.status(401).send(require("../json/2fa/error.json")); + response.status(401).send(require("./json/2fa/error.json")); } }); @@ -29,11 +29,11 @@ router.post("/register", (request, response) => { const { firstName, secondName, phoneNumber, password, role } = request.body; console.log(phoneNumber, password, role); if (phoneNumber === "89999999999" || phoneNumber === "89559999999") { - response.status(401).send(require("../json/register/error.json")); + response.status(401).send(require("./json/register/error.json")); } else if (role === "dogsitter") { - response.send(require("../json/register/dogsitter.success.json")); + response.send(require("./json/register/dogsitter.success.json")); } else { - response.send(require("../json/register/owner.success.json")); + response.send(require("./json/register/owner.success.json")); } }); @@ -58,9 +58,9 @@ router.get("/auth/session", (request, response) => { const decoded = jwt.verify(token, secretKey); if (decoded.role === "dogsitter") { - response.send(require("../json/role/dogsitter.success.json")); + response.send(require("./json/role/dogsitter.success.json")); } else { - response.send(require("../json/role/owner.success.json")); + response.send(require("./json/role/owner.success.json")); } } catch (e) { console.log("token e:", e); From ab92c9932101b2c67dc23a6320549d1fc3f28bfe Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 12:47:43 +0300 Subject: [PATCH 10/41] Uncomment dogsitters app --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 3cfef74..1e81309 100644 --- a/server/index.js +++ b/server/index.js @@ -81,7 +81,7 @@ app.use(require("./root")) app.use("/kfu-m-24-1", require("./routers/kfu-m-24-1")) app.use("/epja-2024-1", require("./routers/epja-2024-1")) app.use("/v1/todo", require("./routers/todo")) -// app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) +app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) app.use("/kazan-explore", require("./routers/kazan-explore")) app.use("/edateam", require("./routers/edateam-legacy")) app.use("/dry-wash", require("./routers/dry-wash")) From 1cf71261d16a35609a3e8f6bcfbce656a6734f70 Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 13:08:55 +0300 Subject: [PATCH 11/41] Remove backend files --- server/routers/dogsitters-finder/auth.js | 74 --------- server/routers/dogsitters-finder/const.js | 3 - .../routers/dogsitters-finder/model/auth.js | 44 ------ .../dogsitters-finder/model/interaction.js | 24 --- .../routers/dogsitters-finder/model/user.js | 83 ---------- server/routers/dogsitters-finder/routes.js | 149 ------------------ 6 files changed, 377 deletions(-) delete mode 100644 server/routers/dogsitters-finder/auth.js delete mode 100644 server/routers/dogsitters-finder/const.js delete mode 100644 server/routers/dogsitters-finder/model/auth.js delete mode 100644 server/routers/dogsitters-finder/model/interaction.js delete mode 100644 server/routers/dogsitters-finder/model/user.js delete mode 100644 server/routers/dogsitters-finder/routes.js diff --git a/server/routers/dogsitters-finder/auth.js b/server/routers/dogsitters-finder/auth.js deleted file mode 100644 index f8c159d..0000000 --- a/server/routers/dogsitters-finder/auth.js +++ /dev/null @@ -1,74 +0,0 @@ -const { Router } = require("express"); -const hash = require("pbkdf2-password")(); -const { promisify } = require("node:util"); -const jwt = require('jsonwebtoken') - -const { getAnswer } = require("../../utils/common"); - -const { AuthModel } = require("./model/todo/auth"); -const { TOKEN_KEY } = require('./const') -const { UserModel } = require("./model/todo/user"); - -const { requiredValidate } = require('./utils') - -const router = Router(); - -router.post( - "/signup", - requiredValidate("login", "password", "email"), - async (req, res, next) => { - const { login, password, email } = req.body - - const user = await AuthModel.findOne({ login }); - - if (user) { - throw new Error("Пользователь с таким логином уже существует"); - } - - hash({ password }, async function (err, pass, salt, hash) { - if (err) return next(err); - - const user = await UserModel.create({ login, email }); - await AuthModel.create({ login, hash, salt, userId: user.id }); - - res.json(getAnswer(null, { ok: true })) - }) - } -) - -function authenticate(login, pass, cb) { - AuthModel.findOne({ login }).populate('userId').exec().then((user) => { - if (!user) return cb(null, null) - - hash({ password: pass, salt: user.salt }, function (err, pass, salt, hash) { - if (err) return cb(err) - if (hash === user.hash) return cb(null, user) - cb(null, null) - }) - }) -} - -const auth = promisify(authenticate) - -router.post('/signin', requiredValidate('login', 'password'), async (req, res) => { - const { login, password } = req.body - - const user = await auth(login, password) - - if (!user) { - throw new Error("Неверный логин или пароль") - } - - const accessToken = jwt.sign({ - ...JSON.parse(JSON.stringify(user.userId)), - }, TOKEN_KEY, { - expiresIn: '12h' - }) - - res.json(getAnswer(null, { - user: user.userId, - token: accessToken, - })) -}) - -module.exports = router diff --git a/server/routers/dogsitters-finder/const.js b/server/routers/dogsitters-finder/const.js deleted file mode 100644 index 7828a91..0000000 --- a/server/routers/dogsitters-finder/const.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.DSF_AUTH_PASSWD_MODEL_NAME = 'DSF_AUTH_PASSWD' -exports.DSF_AUTH_USER_MODEL_NAME = 'DSF_AUTH_USER' -exports.DSF_INTERACTION_MODEL_NAME = 'DSF_INTERACTION' diff --git a/server/routers/dogsitters-finder/model/auth.js b/server/routers/dogsitters-finder/model/auth.js deleted file mode 100644 index 40a8a97..0000000 --- a/server/routers/dogsitters-finder/model/auth.js +++ /dev/null @@ -1,44 +0,0 @@ -const { Schema, model } = require("mongoose"); - -const { - DSF_AUTH_PASSWD_MODEL_NAME, - DSF_AUTH_USER_MODEL_NAME, -} = require("../../const"); - -const schema = new Schema({ - login: { - type: String, - required: true, - unique: true - }, - hash: { - type: String, - required: true - }, - salt: { - type: String, - required: true - }, - userId: { - type: Schema.Types.ObjectId, - ref: DSF_AUTH_USER_MODEL_NAME - }, - created: { - type: Date, - default: () => new Date().toISOString(), - }, -}); - -schema.set("toJSON", { - virtuals: true, - versionKey: false, - transform: function (doc, ret) { - delete ret._id; - }, -}); - -schema.virtual("id").get(function () { - return this._id.toHexString(); -}); - -exports.AuthModel = model(DSF_AUTH_PASSWD_MODEL_NAME, schema); diff --git a/server/routers/dogsitters-finder/model/interaction.js b/server/routers/dogsitters-finder/model/interaction.js deleted file mode 100644 index 95f202b..0000000 --- a/server/routers/dogsitters-finder/model/interaction.js +++ /dev/null @@ -1,24 +0,0 @@ -const { Schema, model } = require("mongoose"); - -const { DSF_AUTH_USER_MODEL_NAME, DSF_INTERACTION_MODEL_NAME } = require("../../const"); - -const interactionSchema = new Schema({ - owner_id: { - type: Schema.Types.ObjectId, - ref: DSF_AUTH_USER_MODEL_NAME, - required: true - }, - dogsitter_id: { - type: Schema.Types.ObjectId, - ref: DSF_AUTH_USER_MODEL_NAME, - required: true - }, - timestamp: { - type: Date, - default: Date.now - } -}); - -interactionSchema.index({ owner_id: 1, dogsitter_id: 1 }); - -module.exports.Interaction = model(DSF_INTERACTION_MODEL_NAME, interactionSchema); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/model/user.js b/server/routers/dogsitters-finder/model/user.js deleted file mode 100644 index 889c946..0000000 --- a/server/routers/dogsitters-finder/model/user.js +++ /dev/null @@ -1,83 +0,0 @@ -const { Schema, model } = require("mongoose"); - -const { DSF_AUTH_USER_MODEL_NAME } = require("../../const"); - -const userSchema = new Schema({ - phone_number: { - type: String, - required: true, - unique: true, - match: /^\+?\d{10,15}$/ - }, - first_name: { - type: String, - required: true, - trim: true - }, - second_name: { - type: String, - required: true, - trim: true - }, - role: { - type: String, - enum: ["dogsitter", "owner"], - required: true - }, - location: { - type: String, - required: function() { - return this.role === "dogsitter"; - } - }, - price: { - type: Number, - min: 0, - required: function() { - return this.role === "dogsitter"; - } - }, - about_me: { - type: String, - maxlength: 500 - }, - rating: { - type: Number, - min: 0, - max: 5, - default: 0 - }, - ratings: { - type: [Number], - default: [], - validate: { - validator: function(arr) { - return arr.every(v => v >= 0 && v <= 5); - }, - message: "Рейтинг должен быть в диапазоне от 0 до 5!" - } - }, - tg: { - type: String, - match: /^[a-zA-Z0-9_]{5,32}$/ - }, - created: { - type: Date, - default: Date.now - } -}); - -userSchema.virtual("id").get(function() { - return this._id.toHexString(); -}); - -userSchema.set("toJSON", { - virtuals: true, - versionKey: false, - transform: function(doc, ret) { - delete ret._id; - delete ret.__v; - } -}); - -module.exports.User = model(DSF_AUTH_USER_MODEL_NAME, userSchema); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/routes.js b/server/routers/dogsitters-finder/routes.js deleted file mode 100644 index f82bc47..0000000 --- a/server/routers/dogsitters-finder/routes.js +++ /dev/null @@ -1,149 +0,0 @@ -const { Router } = require('express') -const { expressjwt } = require('express-jwt') - -const { getAnswer } = require('../../utils/common') -const { User, Interaction } = require('./model') -const { TOKEN_KEY } = require('./const') -const { requiredValidate } = require('./utils') - -const router = Router() - -// Получение списка пользователей -router.get('/users', async (req, res) => { - - const users = await User.find() - .select('-__v -ratings -phone_number') - .lean() - - console.log('get users successfull') - - res.send(getAnswer(null, users)) -}) - -// Получение конкретного пользователя -router.get('/dogsitter-viewing', async (req, res) => { - const { userId } = req.params - - const user = await User.findById(userId) - .select('-__v -ratings') - .lean() - - if (!user) { - return res.status(404).send(getAnswer(new Error('Пользователь не найден'))) - } - - res.send(getAnswer(null, user)) -}) - -router.use(expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] })) - -// Добавление оценки пользователю -router.post('/dogsitter-viewing/rating', requiredValidate('value'), async (req, res) => { - const { userId } = req.params - const { value } = req.body - const authUserId = req.auth.id - - try { - const user = await User.findById(userId) - if (!user) throw new Error('Пользователь не найден') - if (user.role !== 'dogsitter') throw new Error('Нельзя оценивать этого пользователя') - if (user.id === authUserId) throw new Error('Нельзя оценивать самого себя') - - user.ratings.push(Number(value)) - user.rating = user.ratings.reduce((a, b) => a + b, 0) / user.ratings.length - - const updatedUser = await user.save() - - res.send(getAnswer(null, { - id: updatedUser.id, - rating: updatedUser.rating.toFixed(1), - totalRatings: updatedUser.ratings.length - })) - - } catch (error) { - res.status(400).send(getAnswer(error)) - } -}) - -// Обновление информации пользователя -router.patch('/users', async (req, res) => { - const { userId } = req.params - const updates = req.body - - try { - const user = await User.findByIdAndUpdate(userId, updates, { new: true }) - .select('-__v -ratings') - - if (!user) throw new Error('Пользователь не найден') - res.send(getAnswer(null, user)) - - } catch (error) { - res.status(400).send(getAnswer(error)) - } -}) - -// Создание объекта взаимодействия -router.post('/interactions', - expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }), - requiredValidate('dogsitter_id'), - async (req, res) => { - try { - const { dogsitter_id } = req.body - const owner_id = req.auth.id // ID из JWT токена - - // Проверка существования пользователей - const [owner, dogsitter] = await Promise.all([ - User.findById(owner_id), - User.findById(dogsitter_id) - ]) - - if (!owner || owner.role !== 'owner') { - throw new Error('Владелец не найден или имеет неверную роль') - } - - if (!dogsitter || dogsitter.role !== 'dogsitter') { - throw new Error('Догситтер не найден или имеет неверную роль') - } - - // Создание взаимодействия - const interaction = await Interaction.create({ - owner_id, - dogsitter_id - }) - - res.send(getAnswer(null, { - id: interaction.id, - timestamp: interaction.timestamp - })) - - } catch (error) { - res.status(400).send(getAnswer(error)) - } - } -) - -router.get('/interactions/check', async (req, res) => { - const { owner_id, dogsitter_id } = req.query; - - if (!owner_id || !dogsitter_id) { - return res.status(400).send(getAnswer('Missing owner_id or dogsitter_id')); - } - - try { - // Поиск взаимодействий по owner_id и dogsitter_id - const interactions = await Interaction.find({ owner_id, dogsitter_id }) - .select('-__v') // Выбираем только нужные поля - .lean(); - - if (interactions.length === 0) { - return res.status(404).send(getAnswer('No interactions found')); - } - - res.send(getAnswer(null, interactions)); - } catch (error) { - console.error('Error checking interactions:', error); - res.status(500).send(getAnswer('Internal Server Error')); - } -}); - -module.exports = router \ No newline at end of file From 25eee8adf57e49e65a65be383dfdb2fdcdfbe944 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 8 Feb 2025 13:10:58 +0300 Subject: [PATCH 12/41] fix --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 1e81309..3cfef74 100644 --- a/server/index.js +++ b/server/index.js @@ -81,7 +81,7 @@ app.use(require("./root")) app.use("/kfu-m-24-1", require("./routers/kfu-m-24-1")) app.use("/epja-2024-1", require("./routers/epja-2024-1")) app.use("/v1/todo", require("./routers/todo")) -app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) +// app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) app.use("/kazan-explore", require("./routers/kazan-explore")) app.use("/edateam", require("./routers/edateam-legacy")) app.use("/dry-wash", require("./routers/dry-wash")) From 938bd48fff19b9f9546eee7c6bb24a52d195ce37 Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 13:15:02 +0300 Subject: [PATCH 13/41] Fix dogsitters backend --- server/routers/dogsitters-finder/const.js | 2 + .../dogsitters-finder/model/interaction.js | 24 +++ .../routers/dogsitters-finder/model/user.js | 83 ++++++++++ server/routers/dogsitters-finder/routes.js | 149 ++++++++++++++++++ 4 files changed, 258 insertions(+) create mode 100644 server/routers/dogsitters-finder/const.js create mode 100644 server/routers/dogsitters-finder/model/interaction.js create mode 100644 server/routers/dogsitters-finder/model/user.js create mode 100644 server/routers/dogsitters-finder/routes.js diff --git a/server/routers/dogsitters-finder/const.js b/server/routers/dogsitters-finder/const.js new file mode 100644 index 0000000..f10d344 --- /dev/null +++ b/server/routers/dogsitters-finder/const.js @@ -0,0 +1,2 @@ +exports.DSF_AUTH_USER_MODEL_NAME = 'DSF_AUTH_USER' +exports.DSF_INTERACTION_MODEL_NAME = 'DSF_INTERACTION' diff --git a/server/routers/dogsitters-finder/model/interaction.js b/server/routers/dogsitters-finder/model/interaction.js new file mode 100644 index 0000000..95f202b --- /dev/null +++ b/server/routers/dogsitters-finder/model/interaction.js @@ -0,0 +1,24 @@ +const { Schema, model } = require("mongoose"); + +const { DSF_AUTH_USER_MODEL_NAME, DSF_INTERACTION_MODEL_NAME } = require("../../const"); + +const interactionSchema = new Schema({ + owner_id: { + type: Schema.Types.ObjectId, + ref: DSF_AUTH_USER_MODEL_NAME, + required: true + }, + dogsitter_id: { + type: Schema.Types.ObjectId, + ref: DSF_AUTH_USER_MODEL_NAME, + required: true + }, + timestamp: { + type: Date, + default: Date.now + } +}); + +interactionSchema.index({ owner_id: 1, dogsitter_id: 1 }); + +module.exports.Interaction = model(DSF_INTERACTION_MODEL_NAME, interactionSchema); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/model/user.js b/server/routers/dogsitters-finder/model/user.js new file mode 100644 index 0000000..889c946 --- /dev/null +++ b/server/routers/dogsitters-finder/model/user.js @@ -0,0 +1,83 @@ +const { Schema, model } = require("mongoose"); + +const { DSF_AUTH_USER_MODEL_NAME } = require("../../const"); + +const userSchema = new Schema({ + phone_number: { + type: String, + required: true, + unique: true, + match: /^\+?\d{10,15}$/ + }, + first_name: { + type: String, + required: true, + trim: true + }, + second_name: { + type: String, + required: true, + trim: true + }, + role: { + type: String, + enum: ["dogsitter", "owner"], + required: true + }, + location: { + type: String, + required: function() { + return this.role === "dogsitter"; + } + }, + price: { + type: Number, + min: 0, + required: function() { + return this.role === "dogsitter"; + } + }, + about_me: { + type: String, + maxlength: 500 + }, + rating: { + type: Number, + min: 0, + max: 5, + default: 0 + }, + ratings: { + type: [Number], + default: [], + validate: { + validator: function(arr) { + return arr.every(v => v >= 0 && v <= 5); + }, + message: "Рейтинг должен быть в диапазоне от 0 до 5!" + } + }, + tg: { + type: String, + match: /^[a-zA-Z0-9_]{5,32}$/ + }, + created: { + type: Date, + default: Date.now + } +}); + +userSchema.virtual("id").get(function() { + return this._id.toHexString(); +}); + +userSchema.set("toJSON", { + virtuals: true, + versionKey: false, + transform: function(doc, ret) { + delete ret._id; + delete ret.__v; + } +}); + +module.exports.User = model(DSF_AUTH_USER_MODEL_NAME, userSchema); \ No newline at end of file diff --git a/server/routers/dogsitters-finder/routes.js b/server/routers/dogsitters-finder/routes.js new file mode 100644 index 0000000..f82bc47 --- /dev/null +++ b/server/routers/dogsitters-finder/routes.js @@ -0,0 +1,149 @@ +const { Router } = require('express') +const { expressjwt } = require('express-jwt') + +const { getAnswer } = require('../../utils/common') +const { User, Interaction } = require('./model') +const { TOKEN_KEY } = require('./const') +const { requiredValidate } = require('./utils') + +const router = Router() + +// Получение списка пользователей +router.get('/users', async (req, res) => { + + const users = await User.find() + .select('-__v -ratings -phone_number') + .lean() + + console.log('get users successfull') + + res.send(getAnswer(null, users)) +}) + +// Получение конкретного пользователя +router.get('/dogsitter-viewing', async (req, res) => { + const { userId } = req.params + + const user = await User.findById(userId) + .select('-__v -ratings') + .lean() + + if (!user) { + return res.status(404).send(getAnswer(new Error('Пользователь не найден'))) + } + + res.send(getAnswer(null, user)) +}) + +router.use(expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] })) + +// Добавление оценки пользователю +router.post('/dogsitter-viewing/rating', requiredValidate('value'), async (req, res) => { + const { userId } = req.params + const { value } = req.body + const authUserId = req.auth.id + + try { + const user = await User.findById(userId) + if (!user) throw new Error('Пользователь не найден') + if (user.role !== 'dogsitter') throw new Error('Нельзя оценивать этого пользователя') + if (user.id === authUserId) throw new Error('Нельзя оценивать самого себя') + + user.ratings.push(Number(value)) + user.rating = user.ratings.reduce((a, b) => a + b, 0) / user.ratings.length + + const updatedUser = await user.save() + + res.send(getAnswer(null, { + id: updatedUser.id, + rating: updatedUser.rating.toFixed(1), + totalRatings: updatedUser.ratings.length + })) + + } catch (error) { + res.status(400).send(getAnswer(error)) + } +}) + +// Обновление информации пользователя +router.patch('/users', async (req, res) => { + const { userId } = req.params + const updates = req.body + + try { + const user = await User.findByIdAndUpdate(userId, updates, { new: true }) + .select('-__v -ratings') + + if (!user) throw new Error('Пользователь не найден') + res.send(getAnswer(null, user)) + + } catch (error) { + res.status(400).send(getAnswer(error)) + } +}) + +// Создание объекта взаимодействия +router.post('/interactions', + expressjwt({ secret: TOKEN_KEY, algorithms: ['HS256'] }), + requiredValidate('dogsitter_id'), + async (req, res) => { + try { + const { dogsitter_id } = req.body + const owner_id = req.auth.id // ID из JWT токена + + // Проверка существования пользователей + const [owner, dogsitter] = await Promise.all([ + User.findById(owner_id), + User.findById(dogsitter_id) + ]) + + if (!owner || owner.role !== 'owner') { + throw new Error('Владелец не найден или имеет неверную роль') + } + + if (!dogsitter || dogsitter.role !== 'dogsitter') { + throw new Error('Догситтер не найден или имеет неверную роль') + } + + // Создание взаимодействия + const interaction = await Interaction.create({ + owner_id, + dogsitter_id + }) + + res.send(getAnswer(null, { + id: interaction.id, + timestamp: interaction.timestamp + })) + + } catch (error) { + res.status(400).send(getAnswer(error)) + } + } +) + +router.get('/interactions/check', async (req, res) => { + const { owner_id, dogsitter_id } = req.query; + + if (!owner_id || !dogsitter_id) { + return res.status(400).send(getAnswer('Missing owner_id or dogsitter_id')); + } + + try { + // Поиск взаимодействий по owner_id и dogsitter_id + const interactions = await Interaction.find({ owner_id, dogsitter_id }) + .select('-__v') // Выбираем только нужные поля + .lean(); + + if (interactions.length === 0) { + return res.status(404).send(getAnswer('No interactions found')); + } + + res.send(getAnswer(null, interactions)); + } catch (error) { + console.error('Error checking interactions:', error); + res.status(500).send(getAnswer('Internal Server Error')); + } +}); + +module.exports = router \ No newline at end of file From dc99318ff0d485457d5203434c3b762f099f45f2 Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 13:27:38 +0300 Subject: [PATCH 14/41] Fix export --- server/routers/dogsitters-finder/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index 7e8163b..0e7b296 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -66,4 +66,6 @@ router.get("/auth/session", (request, response) => { console.log("token e:", e); return response.status(403).json({ error: "Invalid token" }); } -}); \ No newline at end of file +}); + +module.exports = router \ No newline at end of file From 14f2164a8294b8af52c80118c9293612cb04088b Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 13:44:34 +0300 Subject: [PATCH 15/41] Uncomment dogsitters app --- server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.js b/server/index.js index 3cfef74..1e81309 100644 --- a/server/index.js +++ b/server/index.js @@ -81,7 +81,7 @@ app.use(require("./root")) app.use("/kfu-m-24-1", require("./routers/kfu-m-24-1")) app.use("/epja-2024-1", require("./routers/epja-2024-1")) app.use("/v1/todo", require("./routers/todo")) -// app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) +app.use("/dogsitters-finder", require("./routers/dogsitters-finder")) app.use("/kazan-explore", require("./routers/kazan-explore")) app.use("/edateam", require("./routers/edateam-legacy")) app.use("/dry-wash", require("./routers/dry-wash")) From fadc62c8f08e25f0969e9bc7826d9de9594369ae Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 14:25:29 +0300 Subject: [PATCH 16/41] Update users.json --- .../dogsitters-finder/json/users/users.json | 108 +++++++++++------- 1 file changed, 69 insertions(+), 39 deletions(-) diff --git a/server/routers/dogsitters-finder/json/users/users.json b/server/routers/dogsitters-finder/json/users/users.json index e85d91f..5e70870 100644 --- a/server/routers/dogsitters-finder/json/users/users.json +++ b/server/routers/dogsitters-finder/json/users/users.json @@ -1,39 +1,69 @@ -[ - { - "id": 1, - "phone_number": 89283244141, - "first_name": "Вася", - "second_name": "Пупкин", - "role": "dogsitter", - "location": "Россия, республика Татарстан, Казань, улица Пушкина, 12", - "price": 1500, - "about_me": "Я люблю собак" - }, - { - "id": 2, - "phone_number": 89272844541, - "first_name": "Ваня", - "second_name": "Пуськин", - "role": "dogsitter", - "location": "Россия, республика Татарстан, Казань, улица Абсалямова, 19", - "price": 1000000, - "about_me": "Я не люблю собак. И вообще я котоман." - }, - { - "id": 3, - "phone_number": 89872855893, - "first_name": "Гадий", - "second_name": "Петрович", - "role": "owner" - }, - { - "id": 4, - "phone_number": 89872844591, - "first_name": "Галкин", - "second_name": "Максим", - "role": "dogsitter", - "location": "Россия, республика Татарстан, Казань, проспект Ямашева, 83", - "price": 1000000, - "about_me": "Миллион алых роз" - } -] +{ + "data": [ + { + "id": 1, + "phone_number": "89999999999", + "first_name": "Вася", + "second_name": "Пупкин", + "role": "dogsitter", + "location": "Россия, республика Татарстан, Казань, Пушкина, 12", + "price": "1500", + "about_me": "Я люблю собак!", + "rating": 5, + "ratings": [ + 5, + 5 + ], + "tg": "jullllllie" + }, + { + "id": 2, + "phone_number": 89272844541, + "first_name": "Ваня", + "second_name": "Пуськин", + "role": "dogsitter", + "location": "Россия, республика Татарстан, Казань, улица Абсалямова, 19", + "price": 2000, + "about_me": "Я не люблю собак. И вообще я котоман.", + "rating": 4, + "ratings": [ + 4, + 4 + ], + "tg": "vanya006" + }, + { + "id": 3, + "phone_number": 89559999999, + "first_name": "Гадий", + "second_name": "Петрович", + "role": "owner" + }, + { + "id": 4, + "phone_number": 89872844591, + "first_name": "Галкин", + "second_name": "Максим", + "role": "dogsitter", + "location": "Россия, республика Татарстан, Казань, проспект Ямашева, 83", + "price": 1750, + "about_me": "Миллион алых роз", + "rating": 4.5, + "ratings": [ + 4, + 5 + ], + "tg": "maks100500" + } + ], + "interactions": [ + { + "owner_id": 3, + "dogsitter_id": 4 + }, + { + "owner_id": 1, + "dogsitter_id": 2 + } + ] +} \ No newline at end of file From 26c66f16b48460881f314c8f7e44b16e0cb29f48 Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 14:33:29 +0300 Subject: [PATCH 17/41] Add queries to dogsitter-viewing --- server/routers/dogsitters-finder/index.js | 157 ++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index 0e7b296..075220e 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -68,4 +68,161 @@ router.get("/auth/session", (request, response) => { } }); + +// Проверка взаимодействия между пользователем и догситтером +router.get("/interactions/check", (req, res) => { + const { owner_id, dogsitter_id } = req.query; + + const usersFilePath = path.resolve(__dirname, "../json/users/success.json"); + + delete require.cache[require.resolve(usersFilePath)]; + const usersFile = require(usersFilePath); + + const interactions = usersFile.interactions || []; + + const exists = interactions.some( + (interaction) => + interaction.owner_id === Number(owner_id) && + interaction.dogsitter_id === Number(dogsitter_id) + ); + + res.json({ exists }); +}); + +// Добавление нового взаимодействия +router.post("/interactions", (req, res) => { + const { owner_id, dogsitter_id, interaction_type } = req.body; + + if (!owner_id || !dogsitter_id || !interaction_type) { + return res.status(400).json({ error: "Missing required fields" }); + } + + const usersFilePath = path.resolve(__dirname, "../json/users/success.json"); + + delete require.cache[require.resolve(usersFilePath)]; + const usersFile = require(usersFilePath); + + if (!usersFile.interactions) { + usersFile.interactions = []; + } + + // Проверяем, существует ли уже такое взаимодействие + const exists = usersFile.interactions.some( + (interaction) => + interaction.owner_id === Number(owner_id) && + interaction.dogsitter_id === Number(dogsitter_id) + ); + + if (!exists) { + usersFile.interactions.push({ + owner_id: Number(owner_id), + dogsitter_id: Number(dogsitter_id), + interaction_type, + }); + + fs.writeFileSync( + usersFilePath, + JSON.stringify(usersFile, null, 2), + "utf8" + ); + + console.log( + `Добавлено взаимодействие: owner_id=${owner_id}, dogsitter_id=${dogsitter_id}` + ); + } + + res.json({ success: true }); +}); + +router.get("/dogsitter-viewing", (req, res) => { + const { id } = req.query; + console.log(`Получен запрос для dogsitter с ID: ${id}`); + + const usersFile = require(`../json/users/${stubs.users}.json`); + const users = usersFile.data; // Извлекаем массив из свойства "data" + + const user = users.find((user) => user.id === Number(id)); + + if (user) { + res.json(user); // Возвращаем найденного пользователя + } else { + res.status(404).json({ error: "User not found" }); // Если пользователь не найден + } +}); + +const fs = require('fs'); +const path = require('path'); + + +router.post('/dogsitter-viewing/rating/:id', (req, res) => { + const { id } = req.params; + const { rating } = req.body; + + if (!rating || rating < 1 || rating > 5) { + return res.status(400).json({ error: 'Некорректная оценка' }); + } + + const usersFilePath = path.resolve(__dirname, '../json/users/success.json'); + + delete require.cache[require.resolve(usersFilePath)]; + const usersFile = require(usersFilePath); + const users = usersFile.data; + + const userIndex = users.findIndex(user => user.id === Number(id)); + if (userIndex === -1) { + return res.status(404).json({ error: 'Догситтер не найден' }); + } + + if (!users[userIndex].ratings) { + users[userIndex].ratings = []; + } + users[userIndex].ratings.push(rating); + + if (users[userIndex].ratings.length > 100) { + users[userIndex].ratings.shift(); + } + + const total = users[userIndex].ratings.reduce((sum, r) => sum + r, 0); + users[userIndex].rating = parseFloat((total / users[userIndex].ratings.length).toFixed(2)); + + fs.writeFileSync(usersFilePath, JSON.stringify({ data: users }, null, 2), 'utf8'); + + console.log(`Обновлен рейтинг догситтера ${id}: ${users[userIndex].rating}`); + + res.json({ rating: users[userIndex].rating, ratings: users[userIndex].ratings }); +}); + + +router.patch('/users/:id', (req, res) => { + const { id } = req.params; + const updateData = req.body; + + console.log('Полученные данные для обновления:', updateData); + + + const usersFilePath = path.resolve(__dirname, '../json/users/success.json'); + + delete require.cache[require.resolve(usersFilePath)]; + const usersFile = require(usersFilePath); + const users = usersFile.data; + + const userIndex = users.findIndex((user) => user.id === Number(id)); + if (userIndex === -1) { + return res.status(404).json({ error: 'User not found' }); + } + + users[userIndex] = { ...users[userIndex], ...updateData }; + + fs.writeFileSync( + usersFilePath, + JSON.stringify({ data: users }, null, 2), + 'utf8' + ); + + console.log('Обновлённые данные пользователя:', users[userIndex]); + + res.json(users[userIndex]); +}); + + module.exports = router \ No newline at end of file From a64ac9393593dc2c2fc53b6b02ea18508a702dbc Mon Sep 17 00:00:00 2001 From: Julya Shapaeva Date: Sat, 8 Feb 2025 14:54:57 +0300 Subject: [PATCH 18/41] Fix paths --- server/routers/dogsitters-finder/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/routers/dogsitters-finder/index.js b/server/routers/dogsitters-finder/index.js index 075220e..099ac45 100644 --- a/server/routers/dogsitters-finder/index.js +++ b/server/routers/dogsitters-finder/index.js @@ -73,7 +73,7 @@ router.get("/auth/session", (request, response) => { router.get("/interactions/check", (req, res) => { const { owner_id, dogsitter_id } = req.query; - const usersFilePath = path.resolve(__dirname, "../json/users/success.json"); + const usersFilePath = path.resolve(__dirname, "./json/users/users.json"); delete require.cache[require.resolve(usersFilePath)]; const usersFile = require(usersFilePath); @@ -97,7 +97,7 @@ router.post("/interactions", (req, res) => { return res.status(400).json({ error: "Missing required fields" }); } - const usersFilePath = path.resolve(__dirname, "../json/users/success.json"); + const usersFilePath = path.resolve(__dirname, "./json/users/users.json"); delete require.cache[require.resolve(usersFilePath)]; const usersFile = require(usersFilePath); @@ -138,7 +138,7 @@ router.get("/dogsitter-viewing", (req, res) => { const { id } = req.query; console.log(`Получен запрос для dogsitter с ID: ${id}`); - const usersFile = require(`../json/users/${stubs.users}.json`); + const usersFile = require("./json/users/users.json"); const users = usersFile.data; // Извлекаем массив из свойства "data" const user = users.find((user) => user.id === Number(id)); @@ -162,7 +162,7 @@ router.post('/dogsitter-viewing/rating/:id', (req, res) => { return res.status(400).json({ error: 'Некорректная оценка' }); } - const usersFilePath = path.resolve(__dirname, '../json/users/success.json'); + const usersFilePath = path.resolve(__dirname, "./json/users/users.json"); delete require.cache[require.resolve(usersFilePath)]; const usersFile = require(usersFilePath); @@ -200,7 +200,7 @@ router.patch('/users/:id', (req, res) => { console.log('Полученные данные для обновления:', updateData); - const usersFilePath = path.resolve(__dirname, '../json/users/success.json'); + const usersFilePath = path.resolve(__dirname, "./json/users/users.json"); delete require.cache[require.resolve(usersFilePath)]; const usersFile = require(usersFilePath); From 815f11d5bce21af285211b883f4b14e502fdcc4c Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Mon, 10 Feb 2025 22:07:54 +0300 Subject: [PATCH 19/41] add nav router --- server/routers/todo/index.js | 2 ++ server/routers/todo/nav/index.js | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 server/routers/todo/nav/index.js diff --git a/server/routers/todo/index.js b/server/routers/todo/index.js index b7ca0dc..fb1b02e 100644 --- a/server/routers/todo/index.js +++ b/server/routers/todo/index.js @@ -5,9 +5,11 @@ const router = Router() const todoRouter = require('./routes') const authRouter = require('./auth') const commentRouter = require('./comment') +const navRouter = require('./nav') router.use('/auth', authRouter) router.use('/comment', commentRouter) +router.use('/nav', navRouter) router.use(todoRouter) diff --git a/server/routers/todo/nav/index.js b/server/routers/todo/nav/index.js new file mode 100644 index 0000000..bf776f8 --- /dev/null +++ b/server/routers/todo/nav/index.js @@ -0,0 +1,51 @@ +const router = require("express").Router(); + +router.get("/users", (req, res) => { + res.send({ + success: false, + body: [ + { + id: "some-user-id", + name: "alexandr", + age: 38, + surname: null, + email: null, + rated: 4, + avatar: + "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash", + friends: [ + { + id: "2", + name: "not alexandr", + surname: null, + email: null, + rated: 2, + avatar: "https://www.gravatar.com/avatar/6e?d=robohash", + }, + ], + }, + { + id: "2", + name: "not alexandr", + surname: null, + email: null, + age: 24, + rated: 5, + avatar: "https://www.gravatar.com/avatar/6e?d=robohash", + friends: [ + { + id: "some-user-id", + name: "alexandr", + surname: null, + email: null, + rated: 3, + avatar: + "https://www.gravatar.com/avatar/6529e885535ef67a3fad810ad71167c2c03f79480936e9b3a714731753cbb47e?d=robohash", + }, + ], + }, + ], + }) +}) + +module.exports = router From 752dabd015c92c26789d2f937c67f76a9d1e83f1 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Mon, 10 Feb 2025 22:13:55 +0300 Subject: [PATCH 20/41] fix eslint --- .../kfu-m-24-1/eng-it-lean/gigachat/ai.js | 3147 +++++++++-------- .../eng-it-lean/gigachat/gigachat.js | 5 +- .../kfu-m-24-1/eng-it-lean/gigachat/index.js | 2 +- 3 files changed, 1579 insertions(+), 1575 deletions(-) diff --git a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js index 6b77ac3..ac0faa5 100644 --- a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js +++ b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js @@ -1,24 +1,27 @@ -"use strict"; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; +/* eslint-disable no-undef */ +/* eslint-disable no-empty */ +/* eslint-disable no-async-promise-executor */ +"use strict" +var __defProp = Object.defineProperty +var __getOwnPropDesc = Object.getOwnPropertyDescriptor +var __getOwnPropNames = Object.getOwnPropertyNames +var __hasOwnProp = Object.prototype.hasOwnProperty var __export = (target, all) => { for (var name14 in all) - __defProp(target, name14, { get: all[name14], enumerable: true }); -}; + __defProp(target, name14, { get: all[name14], enumerable: true }) +} var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }) } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + return to +} +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod) // streams/index.ts -var streams_exports = {}; +var streams_exports = {} __export(streams_exports, { AISDKError: () => import_provider16.AISDKError, APICallError: () => import_provider16.APICallError, @@ -77,81 +80,83 @@ __export(streams_exports, { streamObject: () => streamObject, streamText: () => streamText, tool: () => tool -}); -module.exports = __toCommonJS(streams_exports); +}) +module.exports = __toCommonJS(streams_exports) // core/index.ts -var import_provider_utils12 = require("@ai-sdk/provider-utils"); -var import_ui_utils9 = require("@ai-sdk/ui-utils"); +var import_provider_utils12 = require("@ai-sdk/provider-utils") +var import_ui_utils9 = require("@ai-sdk/ui-utils") // core/data-stream/create-data-stream.ts -var import_ui_utils = require("@ai-sdk/ui-utils"); +var import_ui_utils = require("@ai-sdk/ui-utils") function createDataStream({ execute, onError = () => "An error occurred." // mask error messages for safety by default }) { - let controller; - const ongoingStreamPromises = []; + let controller + const ongoingStreamPromises = [] const stream = new ReadableStream({ start(controllerArg) { - controller = controllerArg; + controller = controllerArg } - }); + }) function safeEnqueue(data) { try { - controller.enqueue(data); + controller.enqueue(data) } catch (error) { + console.error(error) } } try { const result = execute({ writeData(data) { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("data", [data])); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("data", [data])) }, writeMessageAnnotation(annotation) { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("message_annotations", [annotation])); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("message_annotations", [annotation])) }, merge(streamArg) { ongoingStreamPromises.push( (async () => { - const reader = streamArg.getReader(); + const reader = streamArg.getReader() while (true) { - const { done, value } = await reader.read(); + const { done, value } = await reader.read() if (done) - break; - safeEnqueue(value); + break + safeEnqueue(value) } })().catch((error) => { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))) }) - ); + ) }, onError - }); + }) if (result) { ongoingStreamPromises.push( result.catch((error) => { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))) }) - ); + ) } } catch (error) { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))) } const waitForStreams = new Promise(async (resolve) => { while (ongoingStreamPromises.length > 0) { - await ongoingStreamPromises.shift(); + await ongoingStreamPromises.shift() } - resolve(); - }); + resolve() + }) waitForStreams.finally(() => { try { - controller.close(); + controller.close() } catch (error) { + console.error(error) } - }); - return stream; + }) + return stream } // core/util/prepare-response-headers.ts @@ -159,14 +164,14 @@ function prepareResponseHeaders(headers, { contentType, dataStreamVersion }) { - const responseHeaders = new Headers(headers != null ? headers : {}); + const responseHeaders = new Headers(headers != null ? headers : {}) if (!responseHeaders.has("Content-Type")) { - responseHeaders.set("Content-Type", contentType); + responseHeaders.set("Content-Type", contentType) } if (dataStreamVersion !== void 0) { - responseHeaders.set("X-Vercel-AI-Data-Stream", dataStreamVersion); + responseHeaders.set("X-Vercel-AI-Data-Stream", dataStreamVersion) } - return responseHeaders; + return responseHeaders } // core/data-stream/create-data-stream-response.ts @@ -187,7 +192,7 @@ function createDataStreamResponse({ dataStreamVersion: "v1" }) } - ); + ) } // core/util/prepare-outgoing-http-headers.ts @@ -195,19 +200,19 @@ function prepareOutgoingHttpHeaders(headers, { contentType, dataStreamVersion }) { - const outgoingHeaders = {}; + const outgoingHeaders = {} if (headers != null) { for (const [key, value] of Object.entries(headers)) { - outgoingHeaders[key] = value; + outgoingHeaders[key] = value } } if (outgoingHeaders["Content-Type"] == null) { - outgoingHeaders["Content-Type"] = contentType; + outgoingHeaders["Content-Type"] = contentType } if (dataStreamVersion !== void 0) { - outgoingHeaders["X-Vercel-AI-Data-Stream"] = dataStreamVersion; + outgoingHeaders["X-Vercel-AI-Data-Stream"] = dataStreamVersion } - return outgoingHeaders; + return outgoingHeaders } // core/util/write-to-server-response.ts @@ -218,23 +223,23 @@ function writeToServerResponse({ headers, stream }) { - response.writeHead(status != null ? status : 200, statusText, headers); - const reader = stream.getReader(); + response.writeHead(status != null ? status : 200, statusText, headers) + const reader = stream.getReader() const read = async () => { try { while (true) { - const { done, value } = await reader.read(); + const { done, value } = await reader.read() if (done) - break; - response.write(value); + break + response.write(value) } } catch (error) { - throw error; + console.error(error) } finally { - response.end(); + response.end() } - }; - read(); + } + read() } // core/data-stream/pipe-data-stream-to-response.ts @@ -256,15 +261,15 @@ function pipeDataStreamToResponse(response, { stream: createDataStream({ execute, onError }).pipeThrough( new TextEncoderStream() ) - }); + }) } // errors/invalid-argument-error.ts -var import_provider = require("@ai-sdk/provider"); -var name = "AI_InvalidArgumentError"; -var marker = `vercel.ai.error.${name}`; -var symbol = Symbol.for(marker); -var _a; +var import_provider = require("@ai-sdk/provider") +var name = "AI_InvalidArgumentError" +var marker = `vercel.ai.error.${name}` +var symbol = Symbol.for(marker) +var _a var InvalidArgumentError = class extends import_provider.AISDKError { constructor({ parameter, @@ -274,49 +279,49 @@ var InvalidArgumentError = class extends import_provider.AISDKError { super({ name, message: `Invalid argument for parameter ${parameter}: ${message}` - }); - this[_a] = true; - this.parameter = parameter; - this.value = value; + }) + this[_a] = true + this.parameter = parameter + this.value = value } static isInstance(error) { - return import_provider.AISDKError.hasMarker(error, marker); + return import_provider.AISDKError.hasMarker(error, marker) } -}; -_a = symbol; +} +_a = symbol // util/retry-with-exponential-backoff.ts -var import_provider3 = require("@ai-sdk/provider"); -var import_provider_utils = require("@ai-sdk/provider-utils"); +var import_provider3 = require("@ai-sdk/provider") +var import_provider_utils = require("@ai-sdk/provider-utils") // util/delay.ts async function delay(delayInMs) { - return delayInMs == null ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayInMs)); + return delayInMs == null ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayInMs)) } // util/retry-error.ts -var import_provider2 = require("@ai-sdk/provider"); -var name2 = "AI_RetryError"; -var marker2 = `vercel.ai.error.${name2}`; -var symbol2 = Symbol.for(marker2); -var _a2; +var import_provider2 = require("@ai-sdk/provider") +var name2 = "AI_RetryError" +var marker2 = `vercel.ai.error.${name2}` +var symbol2 = Symbol.for(marker2) +var _a2 var RetryError = class extends import_provider2.AISDKError { constructor({ message, reason, errors }) { - super({ name: name2, message }); - this[_a2] = true; - this.reason = reason; - this.errors = errors; - this.lastError = errors[errors.length - 1]; + super({ name: name2, message }) + this[_a2] = true + this.reason = reason + this.errors = errors + this.lastError = errors[errors.length - 1] } static isInstance(error) { - return import_provider2.AISDKError.hasMarker(error, marker2); + return import_provider2.AISDKError.hasMarker(error, marker2) } -}; -_a2 = symbol2; +} +_a2 = symbol2 // util/retry-with-exponential-backoff.ts var retryWithExponentialBackoff = ({ @@ -327,47 +332,47 @@ var retryWithExponentialBackoff = ({ maxRetries, delayInMs: initialDelayInMs, backoffFactor -}); +}) async function _retryWithExponentialBackoff(f, { maxRetries, delayInMs, backoffFactor }, errors = []) { try { - return await f(); + return await f() } catch (error) { if ((0, import_provider_utils.isAbortError)(error)) { - throw error; + throw error } if (maxRetries === 0) { - throw error; + throw error } - const errorMessage = (0, import_provider_utils.getErrorMessage)(error); - const newErrors = [...errors, error]; - const tryNumber = newErrors.length; + const errorMessage = (0, import_provider_utils.getErrorMessage)(error) + const newErrors = [...errors, error] + const tryNumber = newErrors.length if (tryNumber > maxRetries) { throw new RetryError({ message: `Failed after ${tryNumber} attempts. Last error: ${errorMessage}`, reason: "maxRetriesExceeded", errors: newErrors - }); + }) } if (error instanceof Error && import_provider3.APICallError.isInstance(error) && error.isRetryable === true && tryNumber <= maxRetries) { - await delay(delayInMs); + await delay(delayInMs) return _retryWithExponentialBackoff( f, { maxRetries, delayInMs: backoffFactor * delayInMs, backoffFactor }, newErrors - ); + ) } if (tryNumber === 1) { - throw error; + throw error } throw new RetryError({ message: `Failed after ${tryNumber} attempts with non-retryable error: '${errorMessage}'`, reason: "errorNotRetryable", errors: newErrors - }); + }) } } @@ -381,21 +386,21 @@ function prepareRetries({ parameter: "maxRetries", value: maxRetries, message: "maxRetries must be an integer" - }); + }) } if (maxRetries < 0) { throw new InvalidArgumentError({ parameter: "maxRetries", value: maxRetries, message: "maxRetries must be >= 0" - }); + }) } } - const maxRetriesResult = maxRetries != null ? maxRetries : 2; + const maxRetriesResult = maxRetries != null ? maxRetries : 2 return { maxRetries: maxRetriesResult, retry: retryWithExponentialBackoff({ maxRetries: maxRetriesResult }) - }; + } } // core/telemetry/assemble-operation-name.ts @@ -410,7 +415,7 @@ function assembleOperationName({ // detailed, AI SDK specific data: "ai.operationId": operationId, "ai.telemetry.functionId": telemetry == null ? void 0 : telemetry.functionId - }; + } } // core/telemetry/get-base-telemetry-attributes.ts @@ -420,93 +425,93 @@ function getBaseTelemetryAttributes({ telemetry, headers }) { - var _a14; + var _a14 return { "ai.model.provider": model.provider, "ai.model.id": model.modelId, // settings: ...Object.entries(settings).reduce((attributes, [key, value]) => { - attributes[`ai.settings.${key}`] = value; - return attributes; + attributes[`ai.settings.${key}`] = value + return attributes }, {}), // add metadata as attributes: ...Object.entries((_a14 = telemetry == null ? void 0 : telemetry.metadata) != null ? _a14 : {}).reduce( (attributes, [key, value]) => { - attributes[`ai.telemetry.metadata.${key}`] = value; - return attributes; + attributes[`ai.telemetry.metadata.${key}`] = value + return attributes }, {} ), // request headers ...Object.entries(headers != null ? headers : {}).reduce((attributes, [key, value]) => { if (value !== void 0) { - attributes[`ai.request.headers.${key}`] = value; + attributes[`ai.request.headers.${key}`] = value } - return attributes; + return attributes }, {}) - }; + } } // core/telemetry/get-tracer.ts -var import_api = require("@opentelemetry/api"); +var import_api = require("@opentelemetry/api") // core/telemetry/noop-tracer.ts var noopTracer = { startSpan() { - return noopSpan; + return noopSpan }, startActiveSpan(name14, arg1, arg2, arg3) { if (typeof arg1 === "function") { - return arg1(noopSpan); + return arg1(noopSpan) } if (typeof arg2 === "function") { - return arg2(noopSpan); + return arg2(noopSpan) } if (typeof arg3 === "function") { - return arg3(noopSpan); + return arg3(noopSpan) } } -}; +} var noopSpan = { spanContext() { - return noopSpanContext; + return noopSpanContext }, setAttribute() { - return this; + return this }, setAttributes() { - return this; + return this }, addEvent() { - return this; + return this }, addLink() { - return this; + return this }, addLinks() { - return this; + return this }, setStatus() { - return this; + return this }, updateName() { - return this; + return this }, end() { - return this; + return this }, isRecording() { - return false; + return false }, recordException() { - return this; + return this } -}; +} var noopSpanContext = { traceId: "", spanId: "", traceFlags: 0 -}; +} // core/telemetry/get-tracer.ts function getTracer({ @@ -514,16 +519,16 @@ function getTracer({ tracer } = {}) { if (!isEnabled) { - return noopTracer; + return noopTracer } if (tracer) { - return tracer; + return tracer } - return import_api.trace.getTracer("ai"); + return import_api.trace.getTracer("ai") } // core/telemetry/record-span.ts -var import_api2 = require("@opentelemetry/api"); +var import_api2 = require("@opentelemetry/api") function recordSpan({ name: name14, tracer, @@ -533,11 +538,11 @@ function recordSpan({ }) { return tracer.startActiveSpan(name14, { attributes }, async (span) => { try { - const result = await fn(span); + const result = await fn(span) if (endWhenDone) { - span.end(); + span.end() } - return result; + return result } catch (error) { try { if (error instanceof Error) { @@ -545,20 +550,20 @@ function recordSpan({ name: error.name, message: error.message, stack: error.stack - }); + }) span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: error.message - }); + }) } else { - span.setStatus({ code: import_api2.SpanStatusCode.ERROR }); + span.setStatus({ code: import_api2.SpanStatusCode.ERROR }) } } finally { - span.end(); + span.end() } - throw error; + throw error } - }); + }) } // core/telemetry/select-telemetry-attributes.ts @@ -567,28 +572,28 @@ function selectTelemetryAttributes({ attributes }) { if ((telemetry == null ? void 0 : telemetry.isEnabled) !== true) { - return {}; + return {} } return Object.entries(attributes).reduce((attributes2, [key, value]) => { if (value === void 0) { - return attributes2; + return attributes2 } if (typeof value === "object" && "input" in value && typeof value.input === "function") { if ((telemetry == null ? void 0 : telemetry.recordInputs) === false) { - return attributes2; + return attributes2 } - const result = value.input(); - return result === void 0 ? attributes2 : { ...attributes2, [key]: result }; + const result = value.input() + return result === void 0 ? attributes2 : { ...attributes2, [key]: result } } if (typeof value === "object" && "output" in value && typeof value.output === "function") { if ((telemetry == null ? void 0 : telemetry.recordOutputs) === false) { - return attributes2; + return attributes2 } - const result = value.output(); - return result === void 0 ? attributes2 : { ...attributes2, [key]: result }; + const result = value.output() + return result === void 0 ? attributes2 : { ...attributes2, [key]: result } } - return { ...attributes2, [key]: value }; - }, {}); + return { ...attributes2, [key]: value } + }, {}) } // core/embed/embed.ts @@ -600,14 +605,14 @@ async function embed({ headers, experimental_telemetry: telemetry }) { - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { maxRetries } - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.embed", attributes: selectTelemetryAttributes({ @@ -639,14 +644,14 @@ async function embed({ }), tracer, fn: async (doEmbedSpan) => { - var _a14; + var _a14 const modelResponse = await model.doEmbed({ values: [value], abortSignal, headers - }); - const embedding2 = modelResponse.embeddings[0]; - const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN }; + }) + const embedding2 = modelResponse.embeddings[0] + const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN } doEmbedSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -659,16 +664,16 @@ async function embed({ "ai.usage.tokens": usage2.tokens } }) - ); + ) return { embedding: embedding2, usage: usage2, rawResponse: modelResponse.rawResponse - }; + } } }) ) - ); + ) span.setAttributes( selectTelemetryAttributes({ telemetry, @@ -677,30 +682,30 @@ async function embed({ "ai.usage.tokens": usage.tokens } }) - ); - return new DefaultEmbedResult({ value, embedding, usage, rawResponse }); + ) + return new DefaultEmbedResult({ value, embedding, usage, rawResponse }) } - }); + }) } var DefaultEmbedResult = class { constructor(options) { - this.value = options.value; - this.embedding = options.embedding; - this.usage = options.usage; - this.rawResponse = options.rawResponse; + this.value = options.value + this.embedding = options.embedding + this.usage = options.usage + this.rawResponse = options.rawResponse } -}; +} // core/util/split-array.ts function splitArray(array, chunkSize) { if (chunkSize <= 0) { - throw new Error("chunkSize must be greater than 0"); + throw new Error("chunkSize must be greater than 0") } - const result = []; + const result = [] for (let i = 0; i < array.length; i += chunkSize) { - result.push(array.slice(i, i + chunkSize)); + result.push(array.slice(i, i + chunkSize)) } - return result; + return result } // core/embed/embed-many.ts @@ -712,14 +717,14 @@ async function embedMany({ headers, experimental_telemetry: telemetry }) { - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { maxRetries } - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.embedMany", attributes: selectTelemetryAttributes({ @@ -735,7 +740,7 @@ async function embedMany({ }), tracer, fn: async (span) => { - const maxEmbeddingsPerCall = model.maxEmbeddingsPerCall; + const maxEmbeddingsPerCall = model.maxEmbeddingsPerCall if (maxEmbeddingsPerCall == null) { const { embeddings: embeddings2, usage } = await retry(() => { return recordSpan({ @@ -756,14 +761,14 @@ async function embedMany({ }), tracer, fn: async (doEmbedSpan) => { - var _a14; + var _a14 const modelResponse = await model.doEmbed({ values, abortSignal, headers - }); - const embeddings3 = modelResponse.embeddings; - const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN }; + }) + const embeddings3 = modelResponse.embeddings + const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN } doEmbedSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -774,11 +779,11 @@ async function embedMany({ "ai.usage.tokens": usage2.tokens } }) - ); - return { embeddings: embeddings3, usage: usage2 }; + ) + return { embeddings: embeddings3, usage: usage2 } } - }); - }); + }) + }) span.setAttributes( selectTelemetryAttributes({ telemetry, @@ -789,12 +794,12 @@ async function embedMany({ "ai.usage.tokens": usage.tokens } }) - ); - return new DefaultEmbedManyResult({ values, embeddings: embeddings2, usage }); + ) + return new DefaultEmbedManyResult({ values, embeddings: embeddings2, usage }) } - const valueChunks = splitArray(values, maxEmbeddingsPerCall); - const embeddings = []; - let tokens = 0; + const valueChunks = splitArray(values, maxEmbeddingsPerCall) + const embeddings = [] + let tokens = 0 for (const chunk of valueChunks) { const { embeddings: responseEmbeddings, usage } = await retry(() => { return recordSpan({ @@ -815,14 +820,14 @@ async function embedMany({ }), tracer, fn: async (doEmbedSpan) => { - var _a14; + var _a14 const modelResponse = await model.doEmbed({ values: chunk, abortSignal, headers - }); - const embeddings2 = modelResponse.embeddings; - const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN }; + }) + const embeddings2 = modelResponse.embeddings + const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN } doEmbedSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -833,13 +838,13 @@ async function embedMany({ "ai.usage.tokens": usage2.tokens } }) - ); - return { embeddings: embeddings2, usage: usage2 }; + ) + return { embeddings: embeddings2, usage: usage2 } } - }); - }); - embeddings.push(...responseEmbeddings); - tokens += usage.tokens; + }) + }) + embeddings.push(...responseEmbeddings) + tokens += usage.tokens } span.setAttributes( selectTelemetryAttributes({ @@ -851,25 +856,25 @@ async function embedMany({ "ai.usage.tokens": tokens } }) - ); + ) return new DefaultEmbedManyResult({ values, embeddings, usage: { tokens } - }); + }) } - }); + }) } var DefaultEmbedManyResult = class { constructor(options) { - this.values = options.values; - this.embeddings = options.embeddings; - this.usage = options.usage; + this.values = options.values + this.embeddings = options.embeddings + this.usage = options.usage } -}; +} // core/generate-image/generate-image.ts -var import_provider_utils2 = require("@ai-sdk/provider-utils"); +var import_provider_utils2 = require("@ai-sdk/provider-utils") async function generateImage({ model, prompt, @@ -882,17 +887,17 @@ async function generateImage({ abortSignal, headers }) { - var _a14; - const { retry } = prepareRetries({ maxRetries: maxRetriesArg }); - const maxImagesPerCall = (_a14 = model.maxImagesPerCall) != null ? _a14 : 1; - const callCount = Math.ceil(n / maxImagesPerCall); + var _a14 + const { retry } = prepareRetries({ maxRetries: maxRetriesArg }) + const maxImagesPerCall = (_a14 = model.maxImagesPerCall) != null ? _a14 : 1 + const callCount = Math.ceil(n / maxImagesPerCall) const callImageCounts = Array.from({ length: callCount }, (_, i) => { if (i < callCount - 1) { - return maxImagesPerCall; + return maxImagesPerCall } - const remainder = n % maxImagesPerCall; - return remainder === 0 ? maxImagesPerCall : remainder; - }); + const remainder = n % maxImagesPerCall + return remainder === 0 ? maxImagesPerCall : remainder + }) const results = await Promise.all( callImageCounts.map( async (callImageCount) => retry( @@ -908,57 +913,57 @@ async function generateImage({ }) ) ) - ); - const images = []; - const warnings = []; + ) + const images = [] + const warnings = [] for (const result of results) { images.push( ...result.images.map((image) => new DefaultGeneratedImage({ image })) - ); - warnings.push(...result.warnings); + ) + warnings.push(...result.warnings) } - return new DefaultGenerateImageResult({ images, warnings }); + return new DefaultGenerateImageResult({ images, warnings }) } var DefaultGenerateImageResult = class { constructor(options) { - this.images = options.images; - this.warnings = options.warnings; + this.images = options.images + this.warnings = options.warnings } get image() { - return this.images[0]; + return this.images[0] } -}; +} var DefaultGeneratedImage = class { constructor({ image }) { - const isUint8Array = image instanceof Uint8Array; - this.base64Data = isUint8Array ? void 0 : image; - this.uint8ArrayData = isUint8Array ? image : void 0; + const isUint8Array = image instanceof Uint8Array + this.base64Data = isUint8Array ? void 0 : image + this.uint8ArrayData = isUint8Array ? image : void 0 } // lazy conversion with caching to avoid unnecessary conversion overhead: get base64() { if (this.base64Data == null) { - this.base64Data = (0, import_provider_utils2.convertUint8ArrayToBase64)(this.uint8ArrayData); + this.base64Data = (0, import_provider_utils2.convertUint8ArrayToBase64)(this.uint8ArrayData) } - return this.base64Data; + return this.base64Data } // lazy conversion with caching to avoid unnecessary conversion overhead: get uint8Array() { if (this.uint8ArrayData == null) { - this.uint8ArrayData = (0, import_provider_utils2.convertBase64ToUint8Array)(this.base64Data); + this.uint8ArrayData = (0, import_provider_utils2.convertBase64ToUint8Array)(this.base64Data) } - return this.uint8ArrayData; + return this.uint8ArrayData } -}; +} // core/generate-object/generate-object.ts -var import_provider_utils6 = require("@ai-sdk/provider-utils"); +var import_provider_utils6 = require("@ai-sdk/provider-utils") // errors/no-object-generated-error.ts -var import_provider4 = require("@ai-sdk/provider"); -var name3 = "AI_NoObjectGeneratedError"; -var marker3 = `vercel.ai.error.${name3}`; -var symbol3 = Symbol.for(marker3); -var _a3; +var import_provider4 = require("@ai-sdk/provider") +var name3 = "AI_NoObjectGeneratedError" +var marker3 = `vercel.ai.error.${name3}` +var symbol3 = Symbol.for(marker3) +var _a3 var NoObjectGeneratedError = class extends import_provider4.AISDKError { constructor({ message = "No object generated.", @@ -967,24 +972,24 @@ var NoObjectGeneratedError = class extends import_provider4.AISDKError { response, usage }) { - super({ name: name3, message, cause }); - this[_a3] = true; - this.text = text2; - this.response = response; - this.usage = usage; + super({ name: name3, message, cause }) + this[_a3] = true + this.text = text2 + this.response = response + this.usage = usage } static isInstance(error) { - return import_provider4.AISDKError.hasMarker(error, marker3); + return import_provider4.AISDKError.hasMarker(error, marker3) } -}; -_a3 = symbol3; +} +_a3 = symbol3 // util/download-error.ts -var import_provider5 = require("@ai-sdk/provider"); -var name4 = "AI_DownloadError"; -var marker4 = `vercel.ai.error.${name4}`; -var symbol4 = Symbol.for(marker4); -var _a4; +var import_provider5 = require("@ai-sdk/provider") +var name4 = "AI_DownloadError" +var marker4 = `vercel.ai.error.${name4}` +var symbol4 = Symbol.for(marker4) +var _a4 var DownloadError = class extends import_provider5.AISDKError { constructor({ url, @@ -993,43 +998,43 @@ var DownloadError = class extends import_provider5.AISDKError { cause, message = cause == null ? `Failed to download ${url}: ${statusCode} ${statusText}` : `Failed to download ${url}: ${cause}` }) { - super({ name: name4, message, cause }); - this[_a4] = true; - this.url = url; - this.statusCode = statusCode; - this.statusText = statusText; + super({ name: name4, message, cause }) + this[_a4] = true + this.url = url + this.statusCode = statusCode + this.statusText = statusText } static isInstance(error) { - return import_provider5.AISDKError.hasMarker(error, marker4); + return import_provider5.AISDKError.hasMarker(error, marker4) } -}; -_a4 = symbol4; +} +_a4 = symbol4 // util/download.ts async function download({ url, fetchImplementation = fetch }) { - var _a14; - const urlText = url.toString(); + var _a14 + const urlText = url.toString() try { - const response = await fetchImplementation(urlText); + const response = await fetchImplementation(urlText) if (!response.ok) { throw new DownloadError({ url: urlText, statusCode: response.status, statusText: response.statusText - }); + }) } return { data: new Uint8Array(await response.arrayBuffer()), mimeType: (_a14 = response.headers.get("content-type")) != null ? _a14 : void 0 - }; + } } catch (error) { if (DownloadError.isInstance(error)) { - throw error; + throw error } - throw new DownloadError({ url: urlText, cause: error }); + throw new DownloadError({ url: urlText, cause: error }) } } @@ -1039,43 +1044,43 @@ var mimeTypeSignatures = [ { mimeType: "image/png", bytes: [137, 80, 78, 71] }, { mimeType: "image/jpeg", bytes: [255, 216] }, { mimeType: "image/webp", bytes: [82, 73, 70, 70] } -]; +] function detectImageMimeType(image) { for (const { bytes, mimeType } of mimeTypeSignatures) { if (image.length >= bytes.length && bytes.every((byte, index) => image[index] === byte)) { - return mimeType; + return mimeType } } - return void 0; + return void 0 } // core/prompt/data-content.ts -var import_provider_utils3 = require("@ai-sdk/provider-utils"); +var import_provider_utils3 = require("@ai-sdk/provider-utils") // core/prompt/invalid-data-content-error.ts -var import_provider6 = require("@ai-sdk/provider"); -var name5 = "AI_InvalidDataContentError"; -var marker5 = `vercel.ai.error.${name5}`; -var symbol5 = Symbol.for(marker5); -var _a5; +var import_provider6 = require("@ai-sdk/provider") +var name5 = "AI_InvalidDataContentError" +var marker5 = `vercel.ai.error.${name5}` +var symbol5 = Symbol.for(marker5) +var _a5 var InvalidDataContentError = class extends import_provider6.AISDKError { constructor({ content, cause, message = `Invalid data content. Expected a base64 string, Uint8Array, ArrayBuffer, or Buffer, but got ${typeof content}.` }) { - super({ name: name5, message, cause }); - this[_a5] = true; - this.content = content; + super({ name: name5, message, cause }) + this[_a5] = true + this.content = content } static isInstance(error) { - return import_provider6.AISDKError.hasMarker(error, marker5); + return import_provider6.AISDKError.hasMarker(error, marker5) } -}; -_a5 = symbol5; +} +_a5 = symbol5 // core/prompt/data-content.ts -var import_zod = require("zod"); +var import_zod = require("zod") var dataContentSchema = import_zod.z.union([ import_zod.z.string(), import_zod.z.instanceof(Uint8Array), @@ -1083,83 +1088,83 @@ var dataContentSchema = import_zod.z.union([ import_zod.z.custom( // Buffer might not be available in some environments such as CloudFlare: (value) => { - var _a14, _b; - return (_b = (_a14 = globalThis.Buffer) == null ? void 0 : _a14.isBuffer(value)) != null ? _b : false; + var _a14, _b + return (_b = (_a14 = globalThis.Buffer) == null ? void 0 : _a14.isBuffer(value)) != null ? _b : false }, { message: "Must be a Buffer" } ) -]); +]) function convertDataContentToBase64String(content) { if (typeof content === "string") { - return content; + return content } if (content instanceof ArrayBuffer) { - return (0, import_provider_utils3.convertUint8ArrayToBase64)(new Uint8Array(content)); + return (0, import_provider_utils3.convertUint8ArrayToBase64)(new Uint8Array(content)) } - return (0, import_provider_utils3.convertUint8ArrayToBase64)(content); + return (0, import_provider_utils3.convertUint8ArrayToBase64)(content) } function convertDataContentToUint8Array(content) { if (content instanceof Uint8Array) { - return content; + return content } if (typeof content === "string") { try { - return (0, import_provider_utils3.convertBase64ToUint8Array)(content); + return (0, import_provider_utils3.convertBase64ToUint8Array)(content) } catch (error) { throw new InvalidDataContentError({ message: "Invalid data content. Content string is not a base64-encoded media.", content, cause: error - }); + }) } } if (content instanceof ArrayBuffer) { - return new Uint8Array(content); + return new Uint8Array(content) } - throw new InvalidDataContentError({ content }); + throw new InvalidDataContentError({ content }) } function convertUint8ArrayToText(uint8Array) { try { - return new TextDecoder().decode(uint8Array); + return new TextDecoder().decode(uint8Array) } catch (error) { - throw new Error("Error decoding Uint8Array to text"); + throw new Error("Error decoding Uint8Array to text") } } // core/prompt/invalid-message-role-error.ts -var import_provider7 = require("@ai-sdk/provider"); -var name6 = "AI_InvalidMessageRoleError"; -var marker6 = `vercel.ai.error.${name6}`; -var symbol6 = Symbol.for(marker6); -var _a6; +var import_provider7 = require("@ai-sdk/provider") +var name6 = "AI_InvalidMessageRoleError" +var marker6 = `vercel.ai.error.${name6}` +var symbol6 = Symbol.for(marker6) +var _a6 var InvalidMessageRoleError = class extends import_provider7.AISDKError { constructor({ role, message = `Invalid message role: '${role}'. Must be one of: "system", "user", "assistant", "tool".` }) { - super({ name: name6, message }); - this[_a6] = true; - this.role = role; + super({ name: name6, message }) + this[_a6] = true + this.role = role } static isInstance(error) { - return import_provider7.AISDKError.hasMarker(error, marker6); + return import_provider7.AISDKError.hasMarker(error, marker6) } -}; -_a6 = symbol6; +} +_a6 = symbol6 // core/prompt/split-data-url.ts function splitDataUrl(dataUrl) { try { - const [header, base64Content] = dataUrl.split(","); + const [header, base64Content] = dataUrl.split(",") return { mimeType: header.split(";")[0].split(":")[1], base64Content - }; + } } catch (error) { return { mimeType: void 0, base64Content: void 0 - }; + } } } @@ -1175,23 +1180,23 @@ async function convertToLanguageModelPrompt({ downloadImplementation, modelSupportsImageUrls, modelSupportsUrl - ); + ) return [ ...prompt.system != null ? [{ role: "system", content: prompt.system }] : [], ...prompt.messages.map( (message) => convertToLanguageModelMessage(message, downloadedAssets) ) - ]; + ] } function convertToLanguageModelMessage(message, downloadedAssets) { - const role = message.role; + const role = message.role switch (role) { case "system": { return { role: "system", content: message.content, providerMetadata: message.experimental_providerMetadata - }; + } } case "user": { if (typeof message.content === "string") { @@ -1199,13 +1204,13 @@ function convertToLanguageModelMessage(message, downloadedAssets) { role: "user", content: [{ type: "text", text: message.content }], providerMetadata: message.experimental_providerMetadata - }; + } } return { role: "user", content: message.content.map((part) => convertPartToLanguageModelPart(part, downloadedAssets)).filter((part) => part.type !== "text" || part.text !== ""), providerMetadata: message.experimental_providerMetadata - }; + } } case "assistant": { if (typeof message.content === "string") { @@ -1213,7 +1218,7 @@ function convertToLanguageModelMessage(message, downloadedAssets) { role: "assistant", content: [{ type: "text", text: message.content }], providerMetadata: message.experimental_providerMetadata - }; + } } return { role: "assistant", @@ -1221,14 +1226,14 @@ function convertToLanguageModelMessage(message, downloadedAssets) { // remove empty text parts: (part) => part.type !== "text" || part.text !== "" ).map((part) => { - const { experimental_providerMetadata, ...rest } = part; + const { experimental_providerMetadata, ...rest } = part return { ...rest, providerMetadata: experimental_providerMetadata - }; + } }), providerMetadata: message.experimental_providerMetadata - }; + } } case "tool": { return { @@ -1243,11 +1248,11 @@ function convertToLanguageModelMessage(message, downloadedAssets) { providerMetadata: part.experimental_providerMetadata })), providerMetadata: message.experimental_providerMetadata - }; + } } default: { - const _exhaustiveCheck = role; - throw new InvalidMessageRoleError({ role: _exhaustiveCheck }); + const _exhaustiveCheck = role + throw new InvalidMessageRoleError({ role: _exhaustiveCheck }) } } } @@ -1263,90 +1268,90 @@ async function downloadAssets(messages, downloadImplementation, modelSupportsIma // support string urls: typeof part === "string" && (part.startsWith("http:") || part.startsWith("https:")) ? new URL(part) : part ) - ).filter((image) => image instanceof URL).filter((url) => !modelSupportsUrl(url)); + ).filter((image) => image instanceof URL).filter((url) => !modelSupportsUrl(url)) const downloadedImages = await Promise.all( urls.map(async (url) => ({ url, data: await downloadImplementation({ url }) })) - ); + ) return Object.fromEntries( downloadedImages.map(({ url, data }) => [url.toString(), data]) - ); + ) } function convertPartToLanguageModelPart(part, downloadedAssets) { - var _a14; + var _a14 if (part.type === "text") { return { type: "text", text: part.text, providerMetadata: part.experimental_providerMetadata - }; + } } - let mimeType = part.mimeType; - let data; - let content; - let normalizedData; - const type = part.type; + let mimeType = part.mimeType + let data + let content + let normalizedData + const type = part.type switch (type) { case "image": - data = part.image; - break; + data = part.image + break case "file": - data = part.data; - break; + data = part.data + break default: - throw new Error(`Unsupported part type: ${type}`); + throw new Error(`Unsupported part type: ${type}`) } try { - content = typeof data === "string" ? new URL(data) : data; + content = typeof data === "string" ? new URL(data) : data } catch (error) { - content = data; + content = data } if (content instanceof URL) { if (content.protocol === "data:") { const { mimeType: dataUrlMimeType, base64Content } = splitDataUrl( content.toString() - ); + ) if (dataUrlMimeType == null || base64Content == null) { - throw new Error(`Invalid data URL format in part ${type}`); + throw new Error(`Invalid data URL format in part ${type}`) } - mimeType = dataUrlMimeType; - normalizedData = convertDataContentToUint8Array(base64Content); + mimeType = dataUrlMimeType + normalizedData = convertDataContentToUint8Array(base64Content) } else { - const downloadedFile = downloadedAssets[content.toString()]; + const downloadedFile = downloadedAssets[content.toString()] if (downloadedFile) { - normalizedData = downloadedFile.data; - mimeType != null ? mimeType : mimeType = downloadedFile.mimeType; + normalizedData = downloadedFile.data + mimeType != null ? mimeType : mimeType = downloadedFile.mimeType } else { - normalizedData = content; + normalizedData = content } } } else { - normalizedData = convertDataContentToUint8Array(content); + normalizedData = convertDataContentToUint8Array(content) } switch (type) { case "image": { if (normalizedData instanceof Uint8Array) { - mimeType = (_a14 = detectImageMimeType(normalizedData)) != null ? _a14 : mimeType; + mimeType = (_a14 = detectImageMimeType(normalizedData)) != null ? _a14 : mimeType } return { type: "image", image: normalizedData, mimeType, providerMetadata: part.experimental_providerMetadata - }; + } } case "file": { if (mimeType == null) { - throw new Error(`Mime type is missing for file part`); + throw new Error(`Mime type is missing for file part`) } return { type: "file", data: normalizedData instanceof Uint8Array ? convertDataContentToBase64String(normalizedData) : normalizedData, mimeType, providerMetadata: part.experimental_providerMetadata - }; + } } } } @@ -1368,14 +1373,14 @@ function prepareCallSettings({ parameter: "maxTokens", value: maxTokens, message: "maxTokens must be an integer" - }); + }) } if (maxTokens < 1) { throw new InvalidArgumentError({ parameter: "maxTokens", value: maxTokens, message: "maxTokens must be >= 1" - }); + }) } } if (temperature != null) { @@ -1384,7 +1389,7 @@ function prepareCallSettings({ parameter: "temperature", value: temperature, message: "temperature must be a number" - }); + }) } } if (topP != null) { @@ -1393,7 +1398,7 @@ function prepareCallSettings({ parameter: "topP", value: topP, message: "topP must be a number" - }); + }) } } if (topK != null) { @@ -1402,7 +1407,7 @@ function prepareCallSettings({ parameter: "topK", value: topK, message: "topK must be a number" - }); + }) } } if (presencePenalty != null) { @@ -1411,7 +1416,7 @@ function prepareCallSettings({ parameter: "presencePenalty", value: presencePenalty, message: "presencePenalty must be a number" - }); + }) } } if (frequencyPenalty != null) { @@ -1420,7 +1425,7 @@ function prepareCallSettings({ parameter: "frequencyPenalty", value: frequencyPenalty, message: "frequencyPenalty must be a number" - }); + }) } } if (seed != null) { @@ -1429,7 +1434,7 @@ function prepareCallSettings({ parameter: "seed", value: seed, message: "seed must be an integer" - }); + }) } } return { @@ -1441,22 +1446,22 @@ function prepareCallSettings({ frequencyPenalty, stopSequences: stopSequences != null && stopSequences.length > 0 ? stopSequences : void 0, seed - }; + } } // core/prompt/standardize-prompt.ts -var import_provider9 = require("@ai-sdk/provider"); -var import_provider_utils4 = require("@ai-sdk/provider-utils"); -var import_zod7 = require("zod"); +var import_provider9 = require("@ai-sdk/provider") +var import_provider_utils4 = require("@ai-sdk/provider-utils") +var import_zod7 = require("zod") // core/prompt/message.ts -var import_zod6 = require("zod"); +var import_zod6 = require("zod") // core/types/provider-metadata.ts -var import_zod3 = require("zod"); +var import_zod3 = require("zod") // core/types/json-value.ts -var import_zod2 = require("zod"); +var import_zod2 = require("zod") var jsonValueSchema = import_zod2.z.lazy( () => import_zod2.z.union([ import_zod2.z.null(), @@ -1466,19 +1471,19 @@ var jsonValueSchema = import_zod2.z.lazy( import_zod2.z.record(import_zod2.z.string(), jsonValueSchema), import_zod2.z.array(jsonValueSchema) ]) -); +) // core/types/provider-metadata.ts var providerMetadataSchema = import_zod3.z.record( import_zod3.z.string(), import_zod3.z.record(import_zod3.z.string(), jsonValueSchema) -); +) // core/prompt/content-part.ts -var import_zod5 = require("zod"); +var import_zod5 = require("zod") // core/prompt/tool-result-content.ts -var import_zod4 = require("zod"); +var import_zod4 = require("zod") var toolResultContentSchema = import_zod4.z.array( import_zod4.z.union([ import_zod4.z.object({ type: import_zod4.z.literal("text"), text: import_zod4.z.string() }), @@ -1488,32 +1493,32 @@ var toolResultContentSchema = import_zod4.z.array( mimeType: import_zod4.z.string().optional() }) ]) -); +) // core/prompt/content-part.ts var textPartSchema = import_zod5.z.object({ type: import_zod5.z.literal("text"), text: import_zod5.z.string(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var imagePartSchema = import_zod5.z.object({ type: import_zod5.z.literal("image"), image: import_zod5.z.union([dataContentSchema, import_zod5.z.instanceof(URL)]), mimeType: import_zod5.z.string().optional(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var filePartSchema = import_zod5.z.object({ type: import_zod5.z.literal("file"), data: import_zod5.z.union([dataContentSchema, import_zod5.z.instanceof(URL)]), mimeType: import_zod5.z.string(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var toolCallPartSchema = import_zod5.z.object({ type: import_zod5.z.literal("tool-call"), toolCallId: import_zod5.z.string(), toolName: import_zod5.z.string(), args: import_zod5.z.unknown() -}); +}) var toolResultPartSchema = import_zod5.z.object({ type: import_zod5.z.literal("tool-result"), toolCallId: import_zod5.z.string(), @@ -1522,14 +1527,14 @@ var toolResultPartSchema = import_zod5.z.object({ content: toolResultContentSchema.optional(), isError: import_zod5.z.boolean().optional(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) // core/prompt/message.ts var coreSystemMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("system"), content: import_zod6.z.string(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreUserMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("user"), content: import_zod6.z.union([ @@ -1537,7 +1542,7 @@ var coreUserMessageSchema = import_zod6.z.object({ import_zod6.z.array(import_zod6.z.union([textPartSchema, imagePartSchema, filePartSchema])) ]), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreAssistantMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("assistant"), content: import_zod6.z.union([ @@ -1545,36 +1550,36 @@ var coreAssistantMessageSchema = import_zod6.z.object({ import_zod6.z.array(import_zod6.z.union([textPartSchema, toolCallPartSchema])) ]), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreToolMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("tool"), content: import_zod6.z.array(toolResultPartSchema), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreMessageSchema = import_zod6.z.union([ coreSystemMessageSchema, coreUserMessageSchema, coreAssistantMessageSchema, coreToolMessageSchema -]); +]) // core/prompt/detect-prompt-type.ts function detectPromptType(prompt) { if (!Array.isArray(prompt)) { - return "other"; + return "other" } if (prompt.length === 0) { - return "messages"; + return "messages" } - const characteristics = prompt.map(detectSingleMessageCharacteristics); + const characteristics = prompt.map(detectSingleMessageCharacteristics) if (characteristics.some((c) => c === "has-ui-specific-parts")) { - return "ui-messages"; + return "ui-messages" } else if (characteristics.every( (c) => c === "has-core-specific-parts" || c === "message" )) { - return "messages"; + return "messages" } else { - return "other"; + return "other" } } function detectSingleMessageCharacteristics(message) { @@ -1582,129 +1587,129 @@ function detectSingleMessageCharacteristics(message) { message.role === "data" || // UI-only role "toolInvocations" in message || // UI-specific field "experimental_attachments" in message)) { - return "has-ui-specific-parts"; + return "has-ui-specific-parts" } else if (typeof message === "object" && message !== null && "content" in message && (Array.isArray(message.content) || // Core messages can have array content "experimental_providerMetadata" in message)) { - return "has-core-specific-parts"; + return "has-core-specific-parts" } else if (typeof message === "object" && message !== null && "role" in message && "content" in message && typeof message.content === "string" && ["system", "user", "assistant", "tool"].includes(message.role)) { - return "message"; + return "message" } else { - return "other"; + return "other" } } // core/prompt/attachments-to-parts.ts function attachmentsToParts(attachments) { - var _a14, _b, _c; - const parts = []; + var _a14, _b, _c + const parts = [] for (const attachment of attachments) { - let url; + let url try { - url = new URL(attachment.url); + url = new URL(attachment.url) } catch (error) { - throw new Error(`Invalid URL: ${attachment.url}`); + throw new Error(`Invalid URL: ${attachment.url}`) } switch (url.protocol) { case "http:": case "https:": { if ((_a14 = attachment.contentType) == null ? void 0 : _a14.startsWith("image/")) { - parts.push({ type: "image", image: url }); + parts.push({ type: "image", image: url }) } else { if (!attachment.contentType) { throw new Error( "If the attachment is not an image, it must specify a content type" - ); + ) } parts.push({ type: "file", data: url, mimeType: attachment.contentType - }); + }) } - break; + break } case "data:": { - let header; - let base64Content; - let mimeType; + let header + let base64Content + let mimeType try { - [header, base64Content] = attachment.url.split(","); - mimeType = header.split(";")[0].split(":")[1]; + [header, base64Content] = attachment.url.split(",") + mimeType = header.split(";")[0].split(":")[1] } catch (error) { - throw new Error(`Error processing data URL: ${attachment.url}`); + throw new Error(`Error processing data URL: ${attachment.url}`) } if (mimeType == null || base64Content == null) { - throw new Error(`Invalid data URL format: ${attachment.url}`); + throw new Error(`Invalid data URL format: ${attachment.url}`) } if ((_b = attachment.contentType) == null ? void 0 : _b.startsWith("image/")) { parts.push({ type: "image", image: convertDataContentToUint8Array(base64Content) - }); + }) } else if ((_c = attachment.contentType) == null ? void 0 : _c.startsWith("text/")) { parts.push({ type: "text", text: convertUint8ArrayToText( convertDataContentToUint8Array(base64Content) ) - }); + }) } else { if (!attachment.contentType) { throw new Error( "If the attachment is not an image or text, it must specify a content type" - ); + ) } parts.push({ type: "file", data: base64Content, mimeType: attachment.contentType - }); + }) } - break; + break } default: { - throw new Error(`Unsupported URL protocol: ${url.protocol}`); + throw new Error(`Unsupported URL protocol: ${url.protocol}`) } } } - return parts; + return parts } // core/prompt/message-conversion-error.ts -var import_provider8 = require("@ai-sdk/provider"); -var name7 = "AI_MessageConversionError"; -var marker7 = `vercel.ai.error.${name7}`; -var symbol7 = Symbol.for(marker7); -var _a7; +var import_provider8 = require("@ai-sdk/provider") +var name7 = "AI_MessageConversionError" +var marker7 = `vercel.ai.error.${name7}` +var symbol7 = Symbol.for(marker7) +var _a7 var MessageConversionError = class extends import_provider8.AISDKError { constructor({ originalMessage, message }) { - super({ name: name7, message }); - this[_a7] = true; - this.originalMessage = originalMessage; + super({ name: name7, message }) + this[_a7] = true + this.originalMessage = originalMessage } static isInstance(error) { - return import_provider8.AISDKError.hasMarker(error, marker7); + return import_provider8.AISDKError.hasMarker(error, marker7) } -}; -_a7 = symbol7; +} +_a7 = symbol7 // core/prompt/convert-to-core-messages.ts function convertToCoreMessages(messages, options) { - var _a14; - const tools = (_a14 = options == null ? void 0 : options.tools) != null ? _a14 : {}; - const coreMessages = []; + var _a14 + const tools = (_a14 = options == null ? void 0 : options.tools) != null ? _a14 : {} + const coreMessages = [] for (const message of messages) { - const { role, content, toolInvocations, experimental_attachments } = message; + const { role, content, toolInvocations, experimental_attachments } = message switch (role) { case "system": { coreMessages.push({ role: "system", content - }); - break; + }) + break } case "user": { coreMessages.push({ @@ -1713,13 +1718,13 @@ function convertToCoreMessages(messages, options) { { type: "text", text: content }, ...attachmentsToParts(experimental_attachments) ] : content - }); - break; + }) + break } case "assistant": { if (toolInvocations == null || toolInvocations.length === 0) { - coreMessages.push({ role: "assistant", content }); - break; + coreMessages.push({ role: "assistant", content }) + break } coreMessages.push({ role: "assistant", @@ -1734,7 +1739,7 @@ function convertToCoreMessages(messages, options) { }) ) ] - }); + }) coreMessages.push({ role: "tool", content: toolInvocations.map((toolInvocation) => { @@ -1742,10 +1747,10 @@ function convertToCoreMessages(messages, options) { throw new MessageConversionError({ originalMessage: message, message: "ToolInvocation must have a result: " + JSON.stringify(toolInvocation) - }); + }) } - const { toolCallId, toolName, result } = toolInvocation; - const tool2 = tools[toolName]; + const { toolCallId, toolName, result } = toolInvocation + const tool2 = tools[toolName] return (tool2 == null ? void 0 : tool2.experimental_toToolResultContent) != null ? { type: "tool-result", toolCallId, @@ -1757,24 +1762,24 @@ function convertToCoreMessages(messages, options) { toolCallId, toolName, result - }; + } }) - }); - break; + }) + break } case "data": { - break; + break } default: { - const _exhaustiveCheck = role; + const _exhaustiveCheck = role throw new MessageConversionError({ originalMessage: message, message: `Unsupported role: ${_exhaustiveCheck}` - }); + }) } } } - return coreMessages; + return coreMessages } // core/prompt/standardize-prompt.ts @@ -1786,26 +1791,26 @@ function standardizePrompt({ throw new import_provider9.InvalidPromptError({ prompt, message: "prompt or messages must be defined" - }); + }) } if (prompt.prompt != null && prompt.messages != null) { throw new import_provider9.InvalidPromptError({ prompt, message: "prompt and messages cannot be defined at the same time" - }); + }) } if (prompt.system != null && typeof prompt.system !== "string") { throw new import_provider9.InvalidPromptError({ prompt, message: "system must be a string" - }); + }) } if (prompt.prompt != null) { if (typeof prompt.prompt !== "string") { throw new import_provider9.InvalidPromptError({ prompt, message: "prompt must be a string" - }); + }) } return { type: "prompt", @@ -1816,37 +1821,37 @@ function standardizePrompt({ content: prompt.prompt } ] - }; + } } if (prompt.messages != null) { - const promptType = detectPromptType(prompt.messages); + const promptType = detectPromptType(prompt.messages) if (promptType === "other") { throw new import_provider9.InvalidPromptError({ prompt, message: "messages must be an array of CoreMessage or UIMessage" - }); + }) } const messages = promptType === "ui-messages" ? convertToCoreMessages(prompt.messages, { tools - }) : prompt.messages; + }) : prompt.messages const validationResult = (0, import_provider_utils4.safeValidateTypes)({ value: messages, schema: import_zod7.z.array(coreMessageSchema) - }); + }) if (!validationResult.success) { throw new import_provider9.InvalidPromptError({ prompt, message: "messages must be an array of CoreMessage or UIMessage", cause: validationResult.error - }); + }) } return { type: "messages", messages, system: prompt.system - }; + } } - throw new Error("unreachable"); + throw new Error("unreachable") } // core/types/usage.ts @@ -1858,20 +1863,20 @@ function calculateLanguageModelUsage({ promptTokens, completionTokens, totalTokens: promptTokens + completionTokens - }; + } } function addLanguageModelUsage(usage1, usage2) { return { promptTokens: usage1.promptTokens + usage2.promptTokens, completionTokens: usage1.completionTokens + usage2.completionTokens, totalTokens: usage1.totalTokens + usage2.totalTokens - }; + } } // core/generate-object/inject-json-instruction.ts -var DEFAULT_SCHEMA_PREFIX = "JSON schema:"; -var DEFAULT_SCHEMA_SUFFIX = "You MUST answer with a JSON object that matches the JSON schema above."; -var DEFAULT_GENERIC_SUFFIX = "You MUST answer with JSON."; +var DEFAULT_SCHEMA_PREFIX = "JSON schema:" +var DEFAULT_SCHEMA_SUFFIX = "You MUST answer with a JSON object that matches the JSON schema above." +var DEFAULT_GENERIC_SUFFIX = "You MUST answer with JSON." function injectJsonInstruction({ prompt, schema, @@ -1885,27 +1890,27 @@ function injectJsonInstruction({ schemaPrefix, schema != null ? JSON.stringify(schema) : void 0, schemaSuffix - ].filter((line) => line != null).join("\n"); + ].filter((line) => line != null).join("\n") } // core/generate-object/output-strategy.ts -var import_provider10 = require("@ai-sdk/provider"); -var import_provider_utils5 = require("@ai-sdk/provider-utils"); -var import_ui_utils2 = require("@ai-sdk/ui-utils"); +var import_provider10 = require("@ai-sdk/provider") +var import_provider_utils5 = require("@ai-sdk/provider-utils") +var import_ui_utils2 = require("@ai-sdk/ui-utils") // core/util/async-iterable-stream.ts function createAsyncIterableStream(source) { - const stream = source.pipeThrough(new TransformStream()); + const stream = source.pipeThrough(new TransformStream()) stream[Symbol.asyncIterator] = () => { - const reader = stream.getReader(); + const reader = stream.getReader() return { async next() { - const { done, value } = await reader.read(); - return done ? { done: true, value: void 0 } : { done: false, value }; + const { done, value } = await reader.read() + return done ? { done: true, value: void 0 } : { done: false, value } } - }; - }; - return stream; + } + } + return stream } // core/generate-object/output-strategy.ts @@ -1913,7 +1918,7 @@ var noSchemaOutputStrategy = { type: "no-schema", jsonSchema: void 0, validatePartialResult({ value, textDelta }) { - return { success: true, value: { partial: value, textDelta } }; + return { success: true, value: { partial: value, textDelta } } }, validateFinalResult(value, context) { return value === void 0 ? { @@ -1924,14 +1929,14 @@ var noSchemaOutputStrategy = { response: context.response, usage: context.usage }) - } : { success: true, value }; + } : { success: true, value } }, createElementStream() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "element streams in no-schema mode" - }); + }) } -}; +} var objectOutputStrategy = (schema) => ({ type: "object", jsonSchema: schema.jsonSchema, @@ -1943,19 +1948,19 @@ var objectOutputStrategy = (schema) => ({ partial: value, textDelta } - }; + } }, validateFinalResult(value) { - return (0, import_provider_utils5.safeValidateTypes)({ value, schema }); + return (0, import_provider_utils5.safeValidateTypes)({ value, schema }) }, createElementStream() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "element streams in object mode" - }); + }) } -}); +}) var arrayOutputStrategy = (schema) => { - const { $schema, ...itemSchema } = schema.jsonSchema; + const { $schema, ...itemSchema } = schema.jsonSchema return { type: "enum", // wrap in object that contains array of elements, since most LLMs will not @@ -1971,7 +1976,7 @@ var arrayOutputStrategy = (schema) => { additionalProperties: false }, validatePartialResult({ value, latestObject, isFirstDelta, isFinalDelta }) { - var _a14; + var _a14 if (!(0, import_provider10.isJSONObject)(value) || !(0, import_provider10.isJSONArray)(value.elements)) { return { success: false, @@ -1979,32 +1984,32 @@ var arrayOutputStrategy = (schema) => { value, cause: "value must be an object that contains an array of elements" }) - }; + } } - const inputArray = value.elements; - const resultArray = []; + const inputArray = value.elements + const resultArray = [] for (let i = 0; i < inputArray.length; i++) { - const element = inputArray[i]; - const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }); + const element = inputArray[i] + const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }) if (i === inputArray.length - 1 && !isFinalDelta) { - continue; + continue } if (!result.success) { - return result; + return result } - resultArray.push(result.value); + resultArray.push(result.value) } - const publishedElementCount = (_a14 = latestObject == null ? void 0 : latestObject.length) != null ? _a14 : 0; - let textDelta = ""; + const publishedElementCount = (_a14 = latestObject == null ? void 0 : latestObject.length) != null ? _a14 : 0 + let textDelta = "" if (isFirstDelta) { - textDelta += "["; + textDelta += "[" } if (publishedElementCount > 0) { - textDelta += ","; + textDelta += "," } - textDelta += resultArray.slice(publishedElementCount).map((element) => JSON.stringify(element)).join(","); + textDelta += resultArray.slice(publishedElementCount).map((element) => JSON.stringify(element)).join(",") if (isFinalDelta) { - textDelta += "]"; + textDelta += "]" } return { success: true, @@ -2012,7 +2017,7 @@ var arrayOutputStrategy = (schema) => { partial: resultArray, textDelta } - }; + } }, validateFinalResult(value) { if (!(0, import_provider10.isJSONObject)(value) || !(0, import_provider10.isJSONArray)(value.elements)) { @@ -2022,51 +2027,51 @@ var arrayOutputStrategy = (schema) => { value, cause: "value must be an object that contains an array of elements" }) - }; - } - const inputArray = value.elements; - for (const element of inputArray) { - const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }); - if (!result.success) { - return result; } } - return { success: true, value: inputArray }; + const inputArray = value.elements + for (const element of inputArray) { + const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }) + if (!result.success) { + return result + } + } + return { success: true, value: inputArray } }, createElementStream(originalStream) { - let publishedElements = 0; + let publishedElements = 0 return createAsyncIterableStream( originalStream.pipeThrough( new TransformStream({ transform(chunk, controller) { switch (chunk.type) { case "object": { - const array = chunk.object; + const array = chunk.object for (; publishedElements < array.length; publishedElements++) { - controller.enqueue(array[publishedElements]); + controller.enqueue(array[publishedElements]) } - break; + break } case "text-delta": case "finish": - break; + break case "error": - controller.error(chunk.error); - break; + controller.error(chunk.error) + break default: { - const _exhaustiveCheck = chunk; + const _exhaustiveCheck = chunk throw new Error( `Unsupported chunk type: ${_exhaustiveCheck}` - ); + ) } } } }) ) - ); + ) } - }; -}; + } +} var enumOutputStrategy = (enumValues) => { return { type: "enum", @@ -2090,29 +2095,29 @@ var enumOutputStrategy = (enumValues) => { value, cause: 'value must be an object that contains a string in the "result" property.' }) - }; + } } - const result = value.result; + const result = value.result return enumValues.includes(result) ? { success: true, value: result } : { success: false, error: new import_provider10.TypeValidationError({ value, cause: "value must be a string in the enum" }) - }; + } }, validatePartialResult() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "partial results in enum mode" - }); + }) }, createElementStream() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "element streams in enum mode" - }); + }) } - }; -}; + } +} function getOutputStrategy({ output, schema, @@ -2120,16 +2125,16 @@ function getOutputStrategy({ }) { switch (output) { case "object": - return objectOutputStrategy((0, import_ui_utils2.asSchema)(schema)); + return objectOutputStrategy((0, import_ui_utils2.asSchema)(schema)) case "array": - return arrayOutputStrategy((0, import_ui_utils2.asSchema)(schema)); + return arrayOutputStrategy((0, import_ui_utils2.asSchema)(schema)) case "enum": - return enumOutputStrategy(enumValues); + return enumOutputStrategy(enumValues) case "no-schema": - return noSchemaOutputStrategy; + return noSchemaOutputStrategy default: { - const _exhaustiveCheck = output; - throw new Error(`Unsupported output: ${_exhaustiveCheck}`); + const _exhaustiveCheck = output + throw new Error(`Unsupported output: ${_exhaustiveCheck}`) } } } @@ -2148,7 +2153,7 @@ function validateObjectGenerationInput({ parameter: "output", value: output, message: "Invalid output type." - }); + }) } if (output === "no-schema") { if (mode === "auto" || mode === "tool") { @@ -2156,35 +2161,35 @@ function validateObjectGenerationInput({ parameter: "mode", value: mode, message: 'Mode must be "json" for no-schema output.' - }); + }) } if (schema != null) { throw new InvalidArgumentError({ parameter: "schema", value: schema, message: "Schema is not supported for no-schema output." - }); + }) } if (schemaDescription != null) { throw new InvalidArgumentError({ parameter: "schemaDescription", value: schemaDescription, message: "Schema description is not supported for no-schema output." - }); + }) } if (schemaName != null) { throw new InvalidArgumentError({ parameter: "schemaName", value: schemaName, message: "Schema name is not supported for no-schema output." - }); + }) } if (enumValues != null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are not supported for no-schema output." - }); + }) } } if (output === "object") { @@ -2193,14 +2198,14 @@ function validateObjectGenerationInput({ parameter: "schema", value: schema, message: "Schema is required for object output." - }); + }) } if (enumValues != null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are not supported for object output." - }); + }) } } if (output === "array") { @@ -2209,14 +2214,14 @@ function validateObjectGenerationInput({ parameter: "schema", value: schema, message: "Element schema is required for array output." - }); + }) } if (enumValues != null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are not supported for array output." - }); + }) } } if (output === "enum") { @@ -2225,28 +2230,28 @@ function validateObjectGenerationInput({ parameter: "schema", value: schema, message: "Schema is not supported for enum output." - }); + }) } if (schemaDescription != null) { throw new InvalidArgumentError({ parameter: "schemaDescription", value: schemaDescription, message: "Schema description is not supported for enum output." - }); + }) } if (schemaName != null) { throw new InvalidArgumentError({ parameter: "schemaName", value: schemaName, message: "Schema name is not supported for enum output." - }); + }) } if (enumValues == null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are required for enum output." - }); + }) } for (const value of enumValues) { if (typeof value !== "string") { @@ -2254,14 +2259,14 @@ function validateObjectGenerationInput({ parameter: "enumValues", value, message: "Enum values must be strings." - }); + }) } } } } // core/generate-object/generate-object.ts -var originalGenerateId = (0, import_provider_utils6.createIdGenerator)({ prefix: "aiobj", size: 24 }); +var originalGenerateId = (0, import_provider_utils6.createIdGenerator)({ prefix: "aiobj", size: 24 }) async function generateObject({ model, enum: enumValues, @@ -2292,23 +2297,23 @@ async function generateObject({ schemaName, schemaDescription, enumValues - }); - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + }) + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const outputStrategy = getOutputStrategy({ output, schema: inputSchema, enumValues - }); + }) if (outputStrategy.type === "no-schema" && mode === void 0) { - mode = "json"; + mode = "json" } const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.generateObject", attributes: selectTelemetryAttributes({ @@ -2332,19 +2337,19 @@ async function generateObject({ }), tracer, fn: async (span) => { - var _a14, _b; + var _a14, _b if (mode === "auto" || mode == null) { - mode = model.defaultObjectGenerationMode; + mode = model.defaultObjectGenerationMode } - let result; - let finishReason; - let usage; - let warnings; - let rawResponse; - let response; - let request; - let logprobs; - let resultProviderMetadata; + let result + let finishReason + let usage + let warnings + let rawResponse + let response + let request + let logprobs + let resultProviderMetadata switch (mode) { case "json": { const standardizedPrompt = standardizePrompt({ @@ -2357,12 +2362,12 @@ async function generateObject({ messages }, tools: void 0 - }); + }) const promptMessages = await convertToLanguageModelPrompt({ prompt: standardizedPrompt, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); + }) const generateResult = await retry( () => recordSpan({ name: "ai.generateObject.doGenerate", @@ -2394,7 +2399,7 @@ async function generateObject({ }), tracer, fn: async (span2) => { - var _a15, _b2, _c, _d, _e, _f; + var _a15, _b2, _c, _d, _e, _f const result2 = await model.doGenerate({ mode: { type: "object-json", @@ -2408,18 +2413,18 @@ async function generateObject({ providerMetadata, abortSignal, headers - }); + }) const responseData = { id: (_b2 = (_a15 = result2.response) == null ? void 0 : _a15.id) != null ? _b2 : generateId3(), timestamp: (_d = (_c = result2.response) == null ? void 0 : _c.timestamp) != null ? _d : currentDate(), modelId: (_f = (_e = result2.response) == null ? void 0 : _e.modelId) != null ? _f : model.modelId - }; + } if (result2.text === void 0) { throw new NoObjectGeneratedError({ message: "No object generated: the model did not return a response.", response: responseData, usage: calculateLanguageModelUsage(result2.usage) - }); + }) } span2.setAttributes( selectTelemetryAttributes({ @@ -2440,33 +2445,33 @@ async function generateObject({ "gen_ai.usage.completion_tokens": result2.usage.completionTokens } }) - ); - return { ...result2, objectText: result2.text, responseData }; + ) + return { ...result2, objectText: result2.text, responseData } } }) - ); - result = generateResult.objectText; - finishReason = generateResult.finishReason; - usage = generateResult.usage; - warnings = generateResult.warnings; - rawResponse = generateResult.rawResponse; - logprobs = generateResult.logprobs; - resultProviderMetadata = generateResult.providerMetadata; - request = (_a14 = generateResult.request) != null ? _a14 : {}; - response = generateResult.responseData; - break; + ) + result = generateResult.objectText + finishReason = generateResult.finishReason + usage = generateResult.usage + warnings = generateResult.warnings + rawResponse = generateResult.rawResponse + logprobs = generateResult.logprobs + resultProviderMetadata = generateResult.providerMetadata + request = (_a14 = generateResult.request) != null ? _a14 : {} + response = generateResult.responseData + break } case "tool": { const standardizedPrompt = standardizePrompt({ prompt: { system, prompt, messages }, tools: void 0 - }); + }) const promptMessages = await convertToLanguageModelPrompt({ prompt: standardizedPrompt, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); - const inputFormat = standardizedPrompt.type; + }) + const inputFormat = standardizedPrompt.type const generateResult = await retry( () => recordSpan({ name: "ai.generateObject.doGenerate", @@ -2498,7 +2503,7 @@ async function generateObject({ }), tracer, fn: async (span2) => { - var _a15, _b2, _c, _d, _e, _f, _g, _h; + var _a15, _b2, _c, _d, _e, _f, _g, _h const result2 = await model.doGenerate({ mode: { type: "object-tool", @@ -2515,19 +2520,19 @@ async function generateObject({ providerMetadata, abortSignal, headers - }); - const objectText = (_b2 = (_a15 = result2.toolCalls) == null ? void 0 : _a15[0]) == null ? void 0 : _b2.args; + }) + const objectText = (_b2 = (_a15 = result2.toolCalls) == null ? void 0 : _a15[0]) == null ? void 0 : _b2.args const responseData = { id: (_d = (_c = result2.response) == null ? void 0 : _c.id) != null ? _d : generateId3(), timestamp: (_f = (_e = result2.response) == null ? void 0 : _e.timestamp) != null ? _f : currentDate(), modelId: (_h = (_g = result2.response) == null ? void 0 : _g.modelId) != null ? _h : model.modelId - }; + } if (objectText === void 0) { throw new NoObjectGeneratedError({ message: "No object generated: the tool was not called.", response: responseData, usage: calculateLanguageModelUsage(result2.usage) - }); + }) } span2.setAttributes( selectTelemetryAttributes({ @@ -2548,33 +2553,33 @@ async function generateObject({ "gen_ai.usage.output_tokens": result2.usage.completionTokens } }) - ); - return { ...result2, objectText, responseData }; + ) + return { ...result2, objectText, responseData } } }) - ); - result = generateResult.objectText; - finishReason = generateResult.finishReason; - usage = generateResult.usage; - warnings = generateResult.warnings; - rawResponse = generateResult.rawResponse; - logprobs = generateResult.logprobs; - resultProviderMetadata = generateResult.providerMetadata; - request = (_b = generateResult.request) != null ? _b : {}; - response = generateResult.responseData; - break; + ) + result = generateResult.objectText + finishReason = generateResult.finishReason + usage = generateResult.usage + warnings = generateResult.warnings + rawResponse = generateResult.rawResponse + logprobs = generateResult.logprobs + resultProviderMetadata = generateResult.providerMetadata + request = (_b = generateResult.request) != null ? _b : {} + response = generateResult.responseData + break } case void 0: { throw new Error( "Model does not have a default object generation mode." - ); + ) } default: { - const _exhaustiveCheck = mode; - throw new Error(`Unsupported mode: ${_exhaustiveCheck}`); + const _exhaustiveCheck = mode + throw new Error(`Unsupported mode: ${_exhaustiveCheck}`) } } - const parseResult = (0, import_provider_utils6.safeParseJSON)({ text: result }); + const parseResult = (0, import_provider_utils6.safeParseJSON)({ text: result }) if (!parseResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: could not parse the response.", @@ -2582,7 +2587,7 @@ async function generateObject({ text: result, response, usage: calculateLanguageModelUsage(usage) - }); + }) } const validationResult = outputStrategy.validateFinalResult( parseResult.value, @@ -2591,7 +2596,7 @@ async function generateObject({ response, usage: calculateLanguageModelUsage(usage) } - ); + ) if (!validationResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: response did not match schema.", @@ -2599,7 +2604,7 @@ async function generateObject({ text: result, response, usage: calculateLanguageModelUsage(usage) - }); + }) } span.setAttributes( selectTelemetryAttributes({ @@ -2613,7 +2618,7 @@ async function generateObject({ "ai.usage.completionTokens": usage.completionTokens } }) - ); + ) return new DefaultGenerateObjectResult({ object: validationResult.value, finishReason, @@ -2626,155 +2631,155 @@ async function generateObject({ }, logprobs, providerMetadata: resultProviderMetadata - }); + }) } - }); + }) } var DefaultGenerateObjectResult = class { constructor(options) { - this.object = options.object; - this.finishReason = options.finishReason; - this.usage = options.usage; - this.warnings = options.warnings; - this.experimental_providerMetadata = options.providerMetadata; - this.response = options.response; - this.request = options.request; - this.logprobs = options.logprobs; + this.object = options.object + this.finishReason = options.finishReason + this.usage = options.usage + this.warnings = options.warnings + this.experimental_providerMetadata = options.providerMetadata + this.response = options.response + this.request = options.request + this.logprobs = options.logprobs } toJsonResponse(init) { - var _a14; + var _a14 return new Response(JSON.stringify(this.object), { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, headers: prepareResponseHeaders(init == null ? void 0 : init.headers, { contentType: "application/json; charset=utf-8" }) - }); + }) } -}; +} // core/generate-object/stream-object.ts -var import_provider_utils7 = require("@ai-sdk/provider-utils"); -var import_ui_utils3 = require("@ai-sdk/ui-utils"); +var import_provider_utils7 = require("@ai-sdk/provider-utils") +var import_ui_utils3 = require("@ai-sdk/ui-utils") // util/delayed-promise.ts var DelayedPromise = class { constructor() { - this.status = { type: "pending" }; - this._resolve = void 0; - this._reject = void 0; + this.status = { type: "pending" } + this._resolve = void 0 + this._reject = void 0 } get value() { if (this.promise) { - return this.promise; + return this.promise } this.promise = new Promise((resolve, reject) => { if (this.status.type === "resolved") { - resolve(this.status.value); + resolve(this.status.value) } else if (this.status.type === "rejected") { - reject(this.status.error); + reject(this.status.error) } - this._resolve = resolve; - this._reject = reject; - }); - return this.promise; + this._resolve = resolve + this._reject = reject + }) + return this.promise } resolve(value) { - var _a14; - this.status = { type: "resolved", value }; + var _a14 + this.status = { type: "resolved", value } if (this.promise) { - (_a14 = this._resolve) == null ? void 0 : _a14.call(this, value); + (_a14 = this._resolve) == null ? void 0 : _a14.call(this, value) } } reject(error) { - var _a14; - this.status = { type: "rejected", error }; + var _a14 + this.status = { type: "rejected", error } if (this.promise) { - (_a14 = this._reject) == null ? void 0 : _a14.call(this, error); + (_a14 = this._reject) == null ? void 0 : _a14.call(this, error) } } -}; +} // util/create-resolvable-promise.ts function createResolvablePromise() { - let resolve; - let reject; + let resolve + let reject const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); + resolve = res + reject = rej + }) return { promise, resolve, reject - }; + } } // core/util/create-stitchable-stream.ts function createStitchableStream() { - let innerStreamReaders = []; - let controller = null; - let isClosed = false; - let waitForNewStream = createResolvablePromise(); + let innerStreamReaders = [] + let controller = null + let isClosed = false + let waitForNewStream = createResolvablePromise() const processPull = async () => { if (isClosed && innerStreamReaders.length === 0) { - controller == null ? void 0 : controller.close(); - return; + controller == null ? void 0 : controller.close() + return } if (innerStreamReaders.length === 0) { - waitForNewStream = createResolvablePromise(); - await waitForNewStream.promise; - return processPull(); + waitForNewStream = createResolvablePromise() + await waitForNewStream.promise + return processPull() } try { - const { value, done } = await innerStreamReaders[0].read(); + const { value, done } = await innerStreamReaders[0].read() if (done) { - innerStreamReaders.shift(); + innerStreamReaders.shift() if (innerStreamReaders.length > 0) { - await processPull(); + await processPull() } else if (isClosed) { - controller == null ? void 0 : controller.close(); + controller == null ? void 0 : controller.close() } } else { - controller == null ? void 0 : controller.enqueue(value); + controller == null ? void 0 : controller.enqueue(value) } } catch (error) { - controller == null ? void 0 : controller.error(error); - innerStreamReaders.shift(); + controller == null ? void 0 : controller.error(error) + innerStreamReaders.shift() if (isClosed && innerStreamReaders.length === 0) { - controller == null ? void 0 : controller.close(); + controller == null ? void 0 : controller.close() } } - }; + } return { stream: new ReadableStream({ start(controllerParam) { - controller = controllerParam; + controller = controllerParam }, pull: processPull, async cancel() { for (const reader of innerStreamReaders) { - await reader.cancel(); + await reader.cancel() } - innerStreamReaders = []; - isClosed = true; + innerStreamReaders = [] + isClosed = true } }), addStream: (innerStream) => { if (isClosed) { - throw new Error("Cannot add inner stream: outer stream is closed"); + throw new Error("Cannot add inner stream: outer stream is closed") } - innerStreamReaders.push(innerStream.getReader()); - waitForNewStream.resolve(); + innerStreamReaders.push(innerStream.getReader()) + waitForNewStream.resolve() }, /** * Gracefully close the outer stream. This will let the inner streams * finish processing and then close the outer stream. */ close: () => { - isClosed = true; - waitForNewStream.resolve(); + isClosed = true + waitForNewStream.resolve() if (innerStreamReaders.length === 0) { - controller == null ? void 0 : controller.close(); + controller == null ? void 0 : controller.close() } }, /** @@ -2782,23 +2787,23 @@ function createStitchableStream() { * and close the outer stream. */ terminate: () => { - isClosed = true; - waitForNewStream.resolve(); - innerStreamReaders.forEach((reader) => reader.cancel()); - innerStreamReaders = []; - controller == null ? void 0 : controller.close(); + isClosed = true + waitForNewStream.resolve() + innerStreamReaders.forEach((reader) => reader.cancel()) + innerStreamReaders = [] + controller == null ? void 0 : controller.close() } - }; + } } // core/util/now.ts function now() { - var _a14, _b; - return (_b = (_a14 = globalThis == null ? void 0 : globalThis.performance) == null ? void 0 : _a14.now()) != null ? _b : Date.now(); + var _a14, _b + return (_b = (_a14 = globalThis == null ? void 0 : globalThis.performance) == null ? void 0 : _a14.now()) != null ? _b : Date.now() } // core/generate-object/stream-object.ts -var originalGenerateId2 = (0, import_provider_utils7.createIdGenerator)({ prefix: "aiobj", size: 24 }); +var originalGenerateId2 = (0, import_provider_utils7.createIdGenerator)({ prefix: "aiobj", size: 24 }) function streamObject({ model, schema: inputSchema, @@ -2828,10 +2833,10 @@ function streamObject({ schema: inputSchema, schemaName, schemaDescription - }); - const outputStrategy = getOutputStrategy({ output, schema: inputSchema }); + }) + const outputStrategy = getOutputStrategy({ output, schema: inputSchema }) if (outputStrategy.type === "no-schema" && mode === void 0) { - mode = "json"; + mode = "json" } return new DefaultStreamObjectResult({ model, @@ -2852,7 +2857,7 @@ function streamObject({ generateId: generateId3, currentDate, now: now2 - }); + }) } var DefaultStreamObjectResult = class { constructor({ @@ -2875,24 +2880,24 @@ var DefaultStreamObjectResult = class { currentDate, now: now2 }) { - this.objectPromise = new DelayedPromise(); - this.usagePromise = new DelayedPromise(); - this.providerMetadataPromise = new DelayedPromise(); - this.warningsPromise = new DelayedPromise(); - this.requestPromise = new DelayedPromise(); - this.responsePromise = new DelayedPromise(); - this.stitchableStream = createStitchableStream(); + this.objectPromise = new DelayedPromise() + this.usagePromise = new DelayedPromise() + this.providerMetadataPromise = new DelayedPromise() + this.warningsPromise = new DelayedPromise() + this.requestPromise = new DelayedPromise() + this.responsePromise = new DelayedPromise() + this.stitchableStream = createStitchableStream() const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg - }); + }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); - const tracer = getTracer(telemetry); - const self = this; + }) + const tracer = getTracer(telemetry) + const self = this recordSpan({ name: "ai.streamObject", attributes: selectTelemetryAttributes({ @@ -2918,10 +2923,10 @@ var DefaultStreamObjectResult = class { endWhenDone: false, fn: async (rootSpan) => { if (mode === "auto" || mode == null) { - mode = model.defaultObjectGenerationMode; + mode = model.defaultObjectGenerationMode } - let callOptions; - let transformer; + let callOptions + let transformer switch (mode) { case "json": { const standardizedPrompt = standardizePrompt({ @@ -2934,7 +2939,7 @@ var DefaultStreamObjectResult = class { messages }, tools: void 0 - }); + }) callOptions = { mode: { type: "object-json", @@ -2952,28 +2957,28 @@ var DefaultStreamObjectResult = class { providerMetadata: inputProviderMetadata, abortSignal, headers - }; + } transformer = { transform: (chunk, controller) => { switch (chunk.type) { case "text-delta": - controller.enqueue(chunk.textDelta); - break; + controller.enqueue(chunk.textDelta) + break case "response-metadata": case "finish": case "error": - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } } - }; - break; + } + break } case "tool": { const standardizedPrompt = standardizePrompt({ prompt: { system, prompt, messages }, tools: void 0 - }); + }) callOptions = { mode: { type: "object-tool", @@ -2994,31 +2999,31 @@ var DefaultStreamObjectResult = class { providerMetadata: inputProviderMetadata, abortSignal, headers - }; + } transformer = { transform(chunk, controller) { switch (chunk.type) { case "tool-call-delta": - controller.enqueue(chunk.argsTextDelta); - break; + controller.enqueue(chunk.argsTextDelta) + break case "response-metadata": case "finish": case "error": - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } } - }; - break; + } + break } case void 0: { throw new Error( "Model does not have a default object generation mode." - ); + ) } default: { - const _exhaustiveCheck = mode; - throw new Error(`Unsupported mode: ${_exhaustiveCheck}`); + const _exhaustiveCheck = mode + throw new Error(`Unsupported mode: ${_exhaustiveCheck}`) } } const { @@ -3062,42 +3067,42 @@ var DefaultStreamObjectResult = class { result: await model.doStream(callOptions) }) }) - ); - self.requestPromise.resolve(request != null ? request : {}); - let usage; - let finishReason; - let providerMetadata; - let object2; - let error; - let accumulatedText = ""; - let textDelta = ""; + ) + self.requestPromise.resolve(request != null ? request : {}) + let usage + let finishReason + let providerMetadata + let object2 + let error + let accumulatedText = "" + let textDelta = "" let response = { id: generateId3(), timestamp: currentDate(), modelId: model.modelId - }; - let latestObjectJson = void 0; - let latestObject = void 0; - let isFirstChunk = true; - let isFirstDelta = true; + } + let latestObjectJson = void 0 + let latestObject = void 0 + let isFirstChunk = true + let isFirstDelta = true const transformedStream = stream.pipeThrough(new TransformStream(transformer)).pipeThrough( new TransformStream({ async transform(chunk, controller) { - var _a14, _b, _c; + var _a14, _b, _c if (isFirstChunk) { - const msToFirstChunk = now2() - startTimestampMs; - isFirstChunk = false; + const msToFirstChunk = now2() - startTimestampMs + isFirstChunk = false doStreamSpan.addEvent("ai.stream.firstChunk", { "ai.stream.msToFirstChunk": msToFirstChunk - }); + }) doStreamSpan.setAttributes({ "ai.stream.msToFirstChunk": msToFirstChunk - }); + }) } if (typeof chunk === "string") { - accumulatedText += chunk; - textDelta += chunk; - const { value: currentObjectJson, state: parseState } = (0, import_ui_utils3.parsePartialJson)(accumulatedText); + accumulatedText += chunk + textDelta += chunk + const { value: currentObjectJson, state: parseState } = (0, import_ui_utils3.parsePartialJson)(accumulatedText) if (currentObjectJson !== void 0 && !(0, import_ui_utils3.isDeepEqualData)(latestObjectJson, currentObjectJson)) { const validationResult = outputStrategy.validatePartialResult({ value: currentObjectJson, @@ -3105,26 +3110,26 @@ var DefaultStreamObjectResult = class { latestObject, isFirstDelta, isFinalDelta: parseState === "successful-parse" - }); + }) if (validationResult.success && !(0, import_ui_utils3.isDeepEqualData)( latestObject, validationResult.value.partial )) { - latestObjectJson = currentObjectJson; - latestObject = validationResult.value.partial; + latestObjectJson = currentObjectJson + latestObject = validationResult.value.partial controller.enqueue({ type: "object", object: latestObject - }); + }) controller.enqueue({ type: "text-delta", textDelta: validationResult.value.textDelta - }); - textDelta = ""; - isFirstDelta = false; + }) + textDelta = "" + isFirstDelta = false } } - return; + return } switch (chunk.type) { case "response-metadata": { @@ -3132,23 +3137,23 @@ var DefaultStreamObjectResult = class { id: (_a14 = chunk.id) != null ? _a14 : response.id, timestamp: (_b = chunk.timestamp) != null ? _b : response.timestamp, modelId: (_c = chunk.modelId) != null ? _c : response.modelId - }; - break; + } + break } case "finish": { if (textDelta !== "") { - controller.enqueue({ type: "text-delta", textDelta }); + controller.enqueue({ type: "text-delta", textDelta }) } - finishReason = chunk.finishReason; - usage = calculateLanguageModelUsage(chunk.usage); - providerMetadata = chunk.providerMetadata; - controller.enqueue({ ...chunk, usage, response }); - self.usagePromise.resolve(usage); - self.providerMetadataPromise.resolve(providerMetadata); + finishReason = chunk.finishReason + usage = calculateLanguageModelUsage(chunk.usage) + providerMetadata = chunk.providerMetadata + controller.enqueue({ ...chunk, usage, response }) + self.usagePromise.resolve(usage) + self.providerMetadataPromise.resolve(providerMetadata) self.responsePromise.resolve({ ...response, headers: rawResponse == null ? void 0 : rawResponse.headers - }); + }) const validationResult = outputStrategy.validateFinalResult( latestObjectJson, { @@ -3156,10 +3161,10 @@ var DefaultStreamObjectResult = class { response, usage } - ); + ) if (validationResult.success) { - object2 = validationResult.value; - self.objectPromise.resolve(object2); + object2 = validationResult.value + self.objectPromise.resolve(object2) } else { error = new NoObjectGeneratedError({ message: "No object generated: response did not match schema.", @@ -3167,14 +3172,14 @@ var DefaultStreamObjectResult = class { text: accumulatedText, response, usage - }); - self.objectPromise.reject(error); + }) + self.objectPromise.reject(error) } - break; + break } default: { - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } } }, @@ -3185,7 +3190,7 @@ var DefaultStreamObjectResult = class { promptTokens: NaN, completionTokens: NaN, totalTokens: NaN - }; + } doStreamSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3207,8 +3212,8 @@ var DefaultStreamObjectResult = class { "gen_ai.usage.output_tokens": finalUsage.completionTokens } }) - ); - doStreamSpan.end(); + ) + doStreamSpan.end() rootSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3220,7 +3225,7 @@ var DefaultStreamObjectResult = class { } } }) - ); + ) await (onFinish == null ? void 0 : onFinish({ usage: finalUsage, object: object2, @@ -3231,47 +3236,47 @@ var DefaultStreamObjectResult = class { }, warnings, experimental_providerMetadata: providerMetadata - })); + })) } catch (error2) { - controller.error(error2); + controller.error(error2) } finally { - rootSpan.end(); + rootSpan.end() } } }) - ); - self.stitchableStream.addStream(transformedStream); + ) + self.stitchableStream.addStream(transformedStream) } }).catch((error) => { self.stitchableStream.addStream( new ReadableStream({ start(controller) { - controller.error(error); + controller.error(error) } }) - ); + ) }).finally(() => { - self.stitchableStream.close(); - }); - this.outputStrategy = outputStrategy; + self.stitchableStream.close() + }) + this.outputStrategy = outputStrategy } get object() { - return this.objectPromise.value; + return this.objectPromise.value } get usage() { - return this.usagePromise.value; + return this.usagePromise.value } get experimental_providerMetadata() { - return this.providerMetadataPromise.value; + return this.providerMetadataPromise.value } get warnings() { - return this.warningsPromise.value; + return this.warningsPromise.value } get request() { - return this.requestPromise.value; + return this.requestPromise.value } get response() { - return this.responsePromise.value; + return this.responsePromise.value } get partialObjectStream() { return createAsyncIterableStream( @@ -3280,28 +3285,28 @@ var DefaultStreamObjectResult = class { transform(chunk, controller) { switch (chunk.type) { case "object": - controller.enqueue(chunk.object); - break; + controller.enqueue(chunk.object) + break case "text-delta": case "finish": - break; + break case "error": - controller.error(chunk.error); - break; + controller.error(chunk.error) + break default: { - const _exhaustiveCheck = chunk; - throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`); + const _exhaustiveCheck = chunk + throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`) } } } }) ) - ); + ) } get elementStream() { return this.outputStrategy.createElementStream( this.stitchableStream.stream - ); + ) } get textStream() { return createAsyncIterableStream( @@ -3310,26 +3315,26 @@ var DefaultStreamObjectResult = class { transform(chunk, controller) { switch (chunk.type) { case "text-delta": - controller.enqueue(chunk.textDelta); - break; + controller.enqueue(chunk.textDelta) + break case "object": case "finish": - break; + break case "error": - controller.error(chunk.error); - break; + controller.error(chunk.error) + break default: { - const _exhaustiveCheck = chunk; - throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`); + const _exhaustiveCheck = chunk + throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`) } } } }) ) - ); + ) } get fullStream() { - return createAsyncIterableStream(this.stitchableStream.stream); + return createAsyncIterableStream(this.stitchableStream.stream) } pipeTextStreamToResponse(response, init) { writeToServerResponse({ @@ -3340,46 +3345,46 @@ var DefaultStreamObjectResult = class { contentType: "text/plain; charset=utf-8" }), stream: this.textStream.pipeThrough(new TextEncoderStream()) - }); + }) } toTextStreamResponse(init) { - var _a14; + var _a14 return new Response(this.textStream.pipeThrough(new TextEncoderStream()), { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, headers: prepareResponseHeaders(init == null ? void 0 : init.headers, { contentType: "text/plain; charset=utf-8" }) - }); + }) } -}; +} // core/generate-text/generate-text.ts -var import_provider_utils9 = require("@ai-sdk/provider-utils"); +var import_provider_utils9 = require("@ai-sdk/provider-utils") // errors/no-output-specified-error.ts -var import_provider11 = require("@ai-sdk/provider"); -var name8 = "AI_NoOutputSpecifiedError"; -var marker8 = `vercel.ai.error.${name8}`; -var symbol8 = Symbol.for(marker8); -var _a8; +var import_provider11 = require("@ai-sdk/provider") +var name8 = "AI_NoOutputSpecifiedError" +var marker8 = `vercel.ai.error.${name8}` +var symbol8 = Symbol.for(marker8) +var _a8 var NoOutputSpecifiedError = class extends import_provider11.AISDKError { // used in isInstance constructor({ message = "No output specified." } = {}) { - super({ name: name8, message }); - this[_a8] = true; + super({ name: name8, message }) + this[_a8] = true } static isInstance(error) { - return import_provider11.AISDKError.hasMarker(error, marker8); + return import_provider11.AISDKError.hasMarker(error, marker8) } -}; -_a8 = symbol8; +} +_a8 = symbol8 // errors/tool-execution-error.ts -var import_provider12 = require("@ai-sdk/provider"); -var name9 = "AI_ToolExecutionError"; -var marker9 = `vercel.ai.error.${name9}`; -var symbol9 = Symbol.for(marker9); -var _a9; +var import_provider12 = require("@ai-sdk/provider") +var name9 = "AI_ToolExecutionError" +var marker9 = `vercel.ai.error.${name9}` +var symbol9 = Symbol.for(marker9) +var _a9 var ToolExecutionError = class extends import_provider12.AISDKError { constructor({ toolArgs, @@ -3388,24 +3393,24 @@ var ToolExecutionError = class extends import_provider12.AISDKError { cause, message = `Error executing tool ${toolName}: ${(0, import_provider12.getErrorMessage)(cause)}` }) { - super({ name: name9, message, cause }); - this[_a9] = true; - this.toolArgs = toolArgs; - this.toolName = toolName; - this.toolCallId = toolCallId; + super({ name: name9, message, cause }) + this[_a9] = true + this.toolArgs = toolArgs + this.toolName = toolName + this.toolCallId = toolCallId } static isInstance(error) { - return import_provider12.AISDKError.hasMarker(error, marker9); + return import_provider12.AISDKError.hasMarker(error, marker9) } -}; -_a9 = symbol9; +} +_a9 = symbol9 // core/prompt/prepare-tools-and-tool-choice.ts -var import_ui_utils4 = require("@ai-sdk/ui-utils"); +var import_ui_utils4 = require("@ai-sdk/ui-utils") // core/util/is-non-empty-object.ts function isNonEmptyObject(object2) { - return object2 != null && Object.keys(object2).length > 0; + return object2 != null && Object.keys(object2).length > 0 } // core/prompt/prepare-tools-and-tool-choice.ts @@ -3418,14 +3423,14 @@ function prepareToolsAndToolChoice({ return { tools: void 0, toolChoice: void 0 - }; + } } const filteredTools = activeTools != null ? Object.entries(tools).filter( ([name14]) => activeTools.includes(name14) - ) : Object.entries(tools); + ) : Object.entries(tools) return { tools: filteredTools.map(([name14, tool2]) => { - const toolType = tool2.type; + const toolType = tool2.type switch (toolType) { case void 0: case "function": @@ -3434,47 +3439,47 @@ function prepareToolsAndToolChoice({ name: name14, description: tool2.description, parameters: (0, import_ui_utils4.asSchema)(tool2.parameters).jsonSchema - }; + } case "provider-defined": return { type: "provider-defined", name: name14, id: tool2.id, args: tool2.args - }; + } default: { - const exhaustiveCheck = toolType; - throw new Error(`Unsupported tool type: ${exhaustiveCheck}`); + const exhaustiveCheck = toolType + throw new Error(`Unsupported tool type: ${exhaustiveCheck}`) } } }), toolChoice: toolChoice == null ? { type: "auto" } : typeof toolChoice === "string" ? { type: toolChoice } : { type: "tool", toolName: toolChoice.toolName } - }; + } } // core/util/split-on-last-whitespace.ts -var lastWhitespaceRegexp = /^([\s\S]*?)(\s+)(\S*)$/; +var lastWhitespaceRegexp = /^([\s\S]*?)(\s+)(\S*)$/ function splitOnLastWhitespace(text2) { - const match = text2.match(lastWhitespaceRegexp); - return match ? { prefix: match[1], whitespace: match[2], suffix: match[3] } : void 0; + const match = text2.match(lastWhitespaceRegexp) + return match ? { prefix: match[1], whitespace: match[2], suffix: match[3] } : void 0 } // core/util/remove-text-after-last-whitespace.ts function removeTextAfterLastWhitespace(text2) { - const match = splitOnLastWhitespace(text2); - return match ? match.prefix + match.whitespace : text2; + const match = splitOnLastWhitespace(text2) + return match ? match.prefix + match.whitespace : text2 } // core/generate-text/parse-tool-call.ts -var import_provider_utils8 = require("@ai-sdk/provider-utils"); -var import_ui_utils5 = require("@ai-sdk/ui-utils"); +var import_provider_utils8 = require("@ai-sdk/provider-utils") +var import_ui_utils5 = require("@ai-sdk/ui-utils") // errors/invalid-tool-arguments-error.ts -var import_provider13 = require("@ai-sdk/provider"); -var name10 = "AI_InvalidToolArgumentsError"; -var marker10 = `vercel.ai.error.${name10}`; -var symbol10 = Symbol.for(marker10); -var _a10; +var import_provider13 = require("@ai-sdk/provider") +var name10 = "AI_InvalidToolArgumentsError" +var marker10 = `vercel.ai.error.${name10}` +var symbol10 = Symbol.for(marker10) +var _a10 var InvalidToolArgumentsError = class extends import_provider13.AISDKError { constructor({ toolArgs, @@ -3484,61 +3489,61 @@ var InvalidToolArgumentsError = class extends import_provider13.AISDKError { cause )}` }) { - super({ name: name10, message, cause }); - this[_a10] = true; - this.toolArgs = toolArgs; - this.toolName = toolName; + super({ name: name10, message, cause }) + this[_a10] = true + this.toolArgs = toolArgs + this.toolName = toolName } static isInstance(error) { - return import_provider13.AISDKError.hasMarker(error, marker10); + return import_provider13.AISDKError.hasMarker(error, marker10) } -}; -_a10 = symbol10; +} +_a10 = symbol10 // errors/no-such-tool-error.ts -var import_provider14 = require("@ai-sdk/provider"); -var name11 = "AI_NoSuchToolError"; -var marker11 = `vercel.ai.error.${name11}`; -var symbol11 = Symbol.for(marker11); -var _a11; +var import_provider14 = require("@ai-sdk/provider") +var name11 = "AI_NoSuchToolError" +var marker11 = `vercel.ai.error.${name11}` +var symbol11 = Symbol.for(marker11) +var _a11 var NoSuchToolError = class extends import_provider14.AISDKError { constructor({ toolName, availableTools = void 0, message = `Model tried to call unavailable tool '${toolName}'. ${availableTools === void 0 ? "No tools are available." : `Available tools: ${availableTools.join(", ")}.`}` }) { - super({ name: name11, message }); - this[_a11] = true; - this.toolName = toolName; - this.availableTools = availableTools; + super({ name: name11, message }) + this[_a11] = true + this.toolName = toolName + this.availableTools = availableTools } static isInstance(error) { - return import_provider14.AISDKError.hasMarker(error, marker11); + return import_provider14.AISDKError.hasMarker(error, marker11) } -}; -_a11 = symbol11; +} +_a11 = symbol11 // errors/tool-call-repair-error.ts -var import_provider15 = require("@ai-sdk/provider"); -var name12 = "AI_ToolCallRepairError"; -var marker12 = `vercel.ai.error.${name12}`; -var symbol12 = Symbol.for(marker12); -var _a12; +var import_provider15 = require("@ai-sdk/provider") +var name12 = "AI_ToolCallRepairError" +var marker12 = `vercel.ai.error.${name12}` +var symbol12 = Symbol.for(marker12) +var _a12 var ToolCallRepairError = class extends import_provider15.AISDKError { constructor({ cause, originalError, message = `Error repairing tool call: ${(0, import_provider15.getErrorMessage)(cause)}` }) { - super({ name: name12, message, cause }); - this[_a12] = true; - this.originalError = originalError; + super({ name: name12, message, cause }) + this[_a12] = true + this.originalError = originalError } static isInstance(error) { - return import_provider15.AISDKError.hasMarker(error, marker12); + return import_provider15.AISDKError.hasMarker(error, marker12) } -}; -_a12 = symbol12; +} +_a12 = symbol12 // core/generate-text/parse-tool-call.ts async function parseToolCall({ @@ -3549,15 +3554,15 @@ async function parseToolCall({ messages }) { if (tools == null) { - throw new NoSuchToolError({ toolName: toolCall.toolName }); + throw new NoSuchToolError({ toolName: toolCall.toolName }) } try { - return await doParseToolCall({ toolCall, tools }); + return await doParseToolCall({ toolCall, tools }) } catch (error) { if (repairToolCall == null || !(NoSuchToolError.isInstance(error) || InvalidToolArgumentsError.isInstance(error))) { - throw error; + throw error } - let repairedToolCall = null; + let repairedToolCall = null try { repairedToolCall = await repairToolCall({ toolCall, @@ -3566,46 +3571,46 @@ async function parseToolCall({ system, messages, error - }); + }) } catch (repairError) { throw new ToolCallRepairError({ cause: repairError, originalError: error - }); + }) } if (repairedToolCall == null) { - throw error; + throw error } - return await doParseToolCall({ toolCall: repairedToolCall, tools }); + return await doParseToolCall({ toolCall: repairedToolCall, tools }) } } async function doParseToolCall({ toolCall, tools }) { - const toolName = toolCall.toolName; - const tool2 = tools[toolName]; + const toolName = toolCall.toolName + const tool2 = tools[toolName] if (tool2 == null) { throw new NoSuchToolError({ toolName: toolCall.toolName, availableTools: Object.keys(tools) - }); + }) } - const schema = (0, import_ui_utils5.asSchema)(tool2.parameters); - const parseResult = toolCall.args.trim() === "" ? (0, import_provider_utils8.safeValidateTypes)({ value: {}, schema }) : (0, import_provider_utils8.safeParseJSON)({ text: toolCall.args, schema }); + const schema = (0, import_ui_utils5.asSchema)(tool2.parameters) + const parseResult = toolCall.args.trim() === "" ? (0, import_provider_utils8.safeValidateTypes)({ value: {}, schema }) : (0, import_provider_utils8.safeParseJSON)({ text: toolCall.args, schema }) if (parseResult.success === false) { throw new InvalidToolArgumentsError({ toolName, toolArgs: toolCall.args, cause: parseResult.error - }); + }) } return { type: "tool-call", toolCallId: toolCall.toolCallId, toolName, args: parseResult.value - }; + } } // core/generate-text/to-response-messages.ts @@ -3617,18 +3622,18 @@ function toResponseMessages({ messageId, generateMessageId }) { - const responseMessages = []; + const responseMessages = [] responseMessages.push({ role: "assistant", content: [{ type: "text", text: text2 }, ...toolCalls], id: messageId - }); + }) if (toolResults.length > 0) { responseMessages.push({ role: "tool", id: generateMessageId(), content: toolResults.map((toolResult) => { - const tool2 = tools[toolResult.toolName]; + const tool2 = tools[toolResult.toolName] return (tool2 == null ? void 0 : tool2.experimental_toToolResultContent) != null ? { type: "tool-result", toolCallId: toolResult.toolCallId, @@ -3642,22 +3647,22 @@ function toResponseMessages({ toolCallId: toolResult.toolCallId, toolName: toolResult.toolName, result: toolResult.result - }; + } }) - }); + }) } - return responseMessages; + return responseMessages } // core/generate-text/generate-text.ts var originalGenerateId3 = (0, import_provider_utils9.createIdGenerator)({ prefix: "aitxt", size: 24 -}); +}) var originalGenerateMessageId = (0, import_provider_utils9.createIdGenerator)({ prefix: "msg", size: 24 -}); +}) async function generateText({ model, tools, @@ -3683,21 +3688,21 @@ async function generateText({ onStepFinish, ...settings }) { - var _a14; + var _a14 if (maxSteps < 1) { throw new InvalidArgumentError({ parameter: "maxSteps", value: maxSteps, message: "maxSteps must be at least 1" - }); + }) } - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); + }) const initialPrompt = standardizePrompt({ prompt: { system: (_a14 = output == null ? void 0 : output.injectIntoSystemPrompt({ system, model })) != null ? _a14 : system, @@ -3705,8 +3710,8 @@ async function generateText({ messages }, tools - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.generateText", attributes: selectTelemetryAttributes({ @@ -3726,31 +3731,31 @@ async function generateText({ }), tracer, fn: async (span) => { - var _a15, _b, _c, _d, _e, _f; + var _a15, _b, _c, _d, _e, _f const mode = { type: "regular", ...prepareToolsAndToolChoice({ tools, toolChoice, activeTools }) - }; - const callSettings = prepareCallSettings(settings); - let currentModelResponse; - let currentToolCalls = []; - let currentToolResults = []; - let stepCount = 0; - const responseMessages = []; - let text2 = ""; - const steps = []; + } + const callSettings = prepareCallSettings(settings) + let currentModelResponse + let currentToolCalls = [] + let currentToolResults = [] + let stepCount = 0 + const responseMessages = [] + let text2 = "" + const steps = [] let usage = { completionTokens: 0, promptTokens: 0, totalTokens: 0 - }; - let stepType = "initial"; + } + let stepType = "initial" do { - const promptFormat = stepCount === 0 ? initialPrompt.type : "messages"; + const promptFormat = stepCount === 0 ? initialPrompt.type : "messages" const stepInputMessages = [ ...initialPrompt.messages, ...responseMessages - ]; + ] const promptMessages = await convertToLanguageModelPrompt({ prompt: { type: promptFormat, @@ -3759,7 +3764,7 @@ async function generateText({ }, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); + }) currentModelResponse = await retry( () => recordSpan({ name: "ai.generateText.doGenerate", @@ -3778,8 +3783,8 @@ async function generateText({ "ai.prompt.tools": { // convert the language model level tools: input: () => { - var _a16; - return (_a16 = mode.tools) == null ? void 0 : _a16.map((tool2) => JSON.stringify(tool2)); + var _a16 + return (_a16 = mode.tools) == null ? void 0 : _a16.map((tool2) => JSON.stringify(tool2)) } }, "ai.prompt.toolChoice": { @@ -3799,7 +3804,7 @@ async function generateText({ }), tracer, fn: async (span2) => { - var _a16, _b2, _c2, _d2, _e2, _f2; + var _a16, _b2, _c2, _d2, _e2, _f2 const result = await model.doGenerate({ mode, ...callSettings, @@ -3809,12 +3814,12 @@ async function generateText({ providerMetadata, abortSignal, headers - }); + }) const responseData = { id: (_b2 = (_a16 = result.response) == null ? void 0 : _a16.id) != null ? _b2 : generateId3(), timestamp: (_d2 = (_c2 = result.response) == null ? void 0 : _c2.timestamp) != null ? _d2 : currentDate(), modelId: (_f2 = (_e2 = result.response) == null ? void 0 : _e2.modelId) != null ? _f2 : model.modelId - }; + } span2.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3839,11 +3844,11 @@ async function generateText({ "gen_ai.usage.output_tokens": result.usage.completionTokens } }) - ); - return { ...result, response: responseData }; + ) + return { ...result, response: responseData } } }) - ); + ) currentToolCalls = await Promise.all( ((_a15 = currentModelResponse.toolCalls) != null ? _a15 : []).map( (toolCall) => parseToolCall({ @@ -3854,7 +3859,7 @@ async function generateText({ messages: stepInputMessages }) ) - ); + ) currentToolResults = tools == null ? [] : await executeTools({ toolCalls: currentToolCalls, tools, @@ -3862,38 +3867,38 @@ async function generateText({ telemetry, messages: stepInputMessages, abortSignal - }); + }) const currentUsage = calculateLanguageModelUsage( currentModelResponse.usage - ); - usage = addLanguageModelUsage(usage, currentUsage); - let nextStepType = "done"; + ) + usage = addLanguageModelUsage(usage, currentUsage) + let nextStepType = "done" if (++stepCount < maxSteps) { if (continueSteps && currentModelResponse.finishReason === "length" && // only use continue when there are no tool calls: currentToolCalls.length === 0) { - nextStepType = "continue"; + nextStepType = "continue" } else if ( // there are tool calls: currentToolCalls.length > 0 && // all current tool calls have results: currentToolResults.length === currentToolCalls.length ) { - nextStepType = "tool-result"; + nextStepType = "tool-result" } } - const originalText = (_b = currentModelResponse.text) != null ? _b : ""; + const originalText = (_b = currentModelResponse.text) != null ? _b : "" const stepTextLeadingWhitespaceTrimmed = stepType === "continue" && // only for continue steps - text2.trimEnd() !== text2 ? originalText.trimStart() : originalText; - const stepText = nextStepType === "continue" ? removeTextAfterLastWhitespace(stepTextLeadingWhitespaceTrimmed) : stepTextLeadingWhitespaceTrimmed; - text2 = nextStepType === "continue" || stepType === "continue" ? text2 + stepText : stepText; + text2.trimEnd() !== text2 ? originalText.trimStart() : originalText + const stepText = nextStepType === "continue" ? removeTextAfterLastWhitespace(stepTextLeadingWhitespaceTrimmed) : stepTextLeadingWhitespaceTrimmed + text2 = nextStepType === "continue" || stepType === "continue" ? text2 + stepText : stepText if (stepType === "continue") { - const lastMessage = responseMessages[responseMessages.length - 1]; + const lastMessage = responseMessages[responseMessages.length - 1] if (typeof lastMessage.content === "string") { - lastMessage.content += stepText; + lastMessage.content += stepText } else { lastMessage.content.push({ text: stepText, type: "text" - }); + }) } } else { responseMessages.push( @@ -3905,7 +3910,7 @@ async function generateText({ messageId: generateMessageId(), generateMessageId }) - ); + ) } const currentStepResult = { stepType, @@ -3925,11 +3930,11 @@ async function generateText({ }, experimental_providerMetadata: currentModelResponse.providerMetadata, isContinued: nextStepType === "continue" - }; - steps.push(currentStepResult); - await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)); - stepType = nextStepType; - } while (stepType !== "done"); + } + steps.push(currentStepResult) + await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)) + stepType = nextStepType + } while (stepType !== "done") span.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3945,17 +3950,17 @@ async function generateText({ "ai.usage.completionTokens": currentModelResponse.usage.completionTokens } }) - ); + ) return new DefaultGenerateTextResult({ text: text2, outputResolver: () => { if (output == null) { - throw new NoOutputSpecifiedError(); + throw new NoOutputSpecifiedError() } return output.parseOutput( { text: text2 }, { response: currentModelResponse.response, usage } - ); + ) }, toolCalls: currentToolCalls, toolResults: currentToolResults, @@ -3971,9 +3976,9 @@ async function generateText({ logprobs: currentModelResponse.logprobs, steps, providerMetadata: currentModelResponse.providerMetadata - }); + }) } - }); + }) } async function executeTools({ toolCalls, @@ -3985,9 +3990,9 @@ async function executeTools({ }) { const toolResults = await Promise.all( toolCalls.map(async ({ toolCallId, toolName, args }) => { - const tool2 = tools[toolName]; + const tool2 = tools[toolName] if ((tool2 == null ? void 0 : tool2.execute) == null) { - return void 0; + return void 0 } const result = await recordSpan({ name: "ai.toolCall", @@ -4012,7 +4017,7 @@ async function executeTools({ toolCallId, messages, abortSignal - }); + }) try { span.setAttributes( selectTelemetryAttributes({ @@ -4023,83 +4028,84 @@ async function executeTools({ } } }) - ); + ) } catch (ignored) { + console.error(ignored) } - return result2; + return result2 } catch (error) { throw new ToolExecutionError({ toolCallId, toolName, toolArgs: args, cause: error - }); + }) } } - }); + }) return { type: "tool-result", toolCallId, toolName, args, result - }; + } }) - ); + ) return toolResults.filter( (result) => result != null - ); + ) } var DefaultGenerateTextResult = class { constructor(options) { - this.text = options.text; - this.toolCalls = options.toolCalls; - this.toolResults = options.toolResults; - this.finishReason = options.finishReason; - this.usage = options.usage; - this.warnings = options.warnings; - this.request = options.request; - this.response = options.response; - this.steps = options.steps; - this.experimental_providerMetadata = options.providerMetadata; - this.logprobs = options.logprobs; - this.outputResolver = options.outputResolver; + this.text = options.text + this.toolCalls = options.toolCalls + this.toolResults = options.toolResults + this.finishReason = options.finishReason + this.usage = options.usage + this.warnings = options.warnings + this.request = options.request + this.response = options.response + this.steps = options.steps + this.experimental_providerMetadata = options.providerMetadata + this.logprobs = options.logprobs + this.outputResolver = options.outputResolver } get experimental_output() { - return this.outputResolver(); + return this.outputResolver() } -}; +} // core/generate-text/output.ts -var output_exports = {}; +var output_exports = {} __export(output_exports, { object: () => object, text: () => text -}); -var import_provider_utils10 = require("@ai-sdk/provider-utils"); -var import_ui_utils6 = require("@ai-sdk/ui-utils"); +}) +var import_provider_utils10 = require("@ai-sdk/provider-utils") +var import_ui_utils6 = require("@ai-sdk/ui-utils") // errors/index.ts -var import_provider16 = require("@ai-sdk/provider"); +var import_provider16 = require("@ai-sdk/provider") // core/generate-text/output.ts var text = () => ({ type: "text", responseFormat: () => ({ type: "text" }), injectIntoSystemPrompt({ system }) { - return system; + return system }, parsePartial({ text: text2 }) { - return { partial: text2 }; + return { partial: text2 } }, parseOutput({ text: text2 }) { - return text2; + return text2 } -}); +}) var object = ({ schema: inputSchema }) => { - const schema = (0, import_ui_utils6.asSchema)(inputSchema); + const schema = (0, import_ui_utils6.asSchema)(inputSchema) return { type: "object", responseFormat: ({ model }) => ({ @@ -4110,28 +4116,28 @@ var object = ({ return model.supportsStructuredOutputs ? system : injectJsonInstruction({ prompt: system, schema: schema.jsonSchema - }); + }) }, parsePartial({ text: text2 }) { - const result = (0, import_ui_utils6.parsePartialJson)(text2); + const result = (0, import_ui_utils6.parsePartialJson)(text2) switch (result.state) { case "failed-parse": case "undefined-input": - return void 0; + return void 0 case "repaired-parse": case "successful-parse": return { // Note: currently no validation of partial results: partial: result.value - }; + } default: { - const _exhaustiveCheck = result.state; - throw new Error(`Unsupported parse state: ${_exhaustiveCheck}`); + const _exhaustiveCheck = result.state + throw new Error(`Unsupported parse state: ${_exhaustiveCheck}`) } } }, parseOutput({ text: text2 }, context) { - const parseResult = (0, import_provider_utils10.safeParseJSON)({ text: text2 }); + const parseResult = (0, import_provider_utils10.safeParseJSON)({ text: text2 }) if (!parseResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: could not parse the response.", @@ -4139,12 +4145,12 @@ var object = ({ text: text2, response: context.response, usage: context.usage - }); + }) } const validationResult = (0, import_provider_utils10.safeValidateTypes)({ value: parseResult.value, schema - }); + }) if (!validationResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: response did not match schema.", @@ -4152,159 +4158,159 @@ var object = ({ text: text2, response: context.response, usage: context.usage - }); + }) } - return validationResult.value; + return validationResult.value } - }; -}; + } +} // core/generate-text/smooth-stream.ts -var import_provider17 = require("@ai-sdk/provider"); +var import_provider17 = require("@ai-sdk/provider") var CHUNKING_REGEXPS = { word: /\s*\S+\s+/m, line: /[^\n]*\n/m -}; +} function smoothStream({ delayInMs = 10, chunking = "word", _internal: { delay: delay2 = delay } = {} } = {}) { - const chunkingRegexp = typeof chunking === "string" ? CHUNKING_REGEXPS[chunking] : chunking; + const chunkingRegexp = typeof chunking === "string" ? CHUNKING_REGEXPS[chunking] : chunking if (chunkingRegexp == null) { throw new import_provider17.InvalidArgumentError({ argument: "chunking", message: `Chunking must be "word" or "line" or a RegExp. Received: ${chunking}` - }); + }) } return () => { - let buffer = ""; + let buffer = "" return new TransformStream({ async transform(chunk, controller) { if (chunk.type === "step-finish") { if (buffer.length > 0) { - controller.enqueue({ type: "text-delta", textDelta: buffer }); - buffer = ""; + controller.enqueue({ type: "text-delta", textDelta: buffer }) + buffer = "" } - controller.enqueue(chunk); - return; + controller.enqueue(chunk) + return } if (chunk.type !== "text-delta") { - controller.enqueue(chunk); - return; + controller.enqueue(chunk) + return } - buffer += chunk.textDelta; - let match; + buffer += chunk.textDelta + let match while ((match = chunkingRegexp.exec(buffer)) != null) { - const chunk2 = match[0]; - controller.enqueue({ type: "text-delta", textDelta: chunk2 }); - buffer = buffer.slice(chunk2.length); - await delay2(delayInMs); + const chunk2 = match[0] + controller.enqueue({ type: "text-delta", textDelta: chunk2 }) + buffer = buffer.slice(chunk2.length) + await delay2(delayInMs) } } - }); - }; + }) + } } // core/generate-text/stream-text.ts -var import_provider_utils11 = require("@ai-sdk/provider-utils"); -var import_ui_utils8 = require("@ai-sdk/ui-utils"); +var import_provider_utils11 = require("@ai-sdk/provider-utils") +var import_ui_utils8 = require("@ai-sdk/ui-utils") // util/as-array.ts function asArray(value) { - return value === void 0 ? [] : Array.isArray(value) ? value : [value]; + return value === void 0 ? [] : Array.isArray(value) ? value : [value] } // core/util/merge-streams.ts function mergeStreams(stream1, stream2) { - const reader1 = stream1.getReader(); - const reader2 = stream2.getReader(); - let lastRead1 = void 0; - let lastRead2 = void 0; - let stream1Done = false; - let stream2Done = false; + const reader1 = stream1.getReader() + const reader2 = stream2.getReader() + let lastRead1 = void 0 + let lastRead2 = void 0 + let stream1Done = false + let stream2Done = false async function readStream1(controller) { try { if (lastRead1 == null) { - lastRead1 = reader1.read(); + lastRead1 = reader1.read() } - const result = await lastRead1; - lastRead1 = void 0; + const result = await lastRead1 + lastRead1 = void 0 if (!result.done) { - controller.enqueue(result.value); + controller.enqueue(result.value) } else { - controller.close(); + controller.close() } } catch (error) { - controller.error(error); + controller.error(error) } } async function readStream2(controller) { try { if (lastRead2 == null) { - lastRead2 = reader2.read(); + lastRead2 = reader2.read() } - const result = await lastRead2; - lastRead2 = void 0; + const result = await lastRead2 + lastRead2 = void 0 if (!result.done) { - controller.enqueue(result.value); + controller.enqueue(result.value) } else { - controller.close(); + controller.close() } } catch (error) { - controller.error(error); + controller.error(error) } } return new ReadableStream({ async pull(controller) { try { if (stream1Done) { - await readStream2(controller); - return; + await readStream2(controller) + return } if (stream2Done) { - await readStream1(controller); - return; + await readStream1(controller) + return } if (lastRead1 == null) { - lastRead1 = reader1.read(); + lastRead1 = reader1.read() } if (lastRead2 == null) { - lastRead2 = reader2.read(); + lastRead2 = reader2.read() } const { result, reader } = await Promise.race([ lastRead1.then((result2) => ({ result: result2, reader: reader1 })), lastRead2.then((result2) => ({ result: result2, reader: reader2 })) - ]); + ]) if (!result.done) { - controller.enqueue(result.value); + controller.enqueue(result.value) } if (reader === reader1) { - lastRead1 = void 0; + lastRead1 = void 0 if (result.done) { - await readStream2(controller); - stream1Done = true; + await readStream2(controller) + stream1Done = true } } else { - lastRead2 = void 0; + lastRead2 = void 0 if (result.done) { - stream2Done = true; - await readStream1(controller); + stream2Done = true + await readStream1(controller) } } } catch (error) { - controller.error(error); + controller.error(error) } }, cancel() { - reader1.cancel(); - reader2.cancel(); + reader1.cancel() + reader2.cancel() } - }); + }) } // core/generate-text/run-tools-transformation.ts -var import_ui_utils7 = require("@ai-sdk/ui-utils"); +var import_ui_utils7 = require("@ai-sdk/ui-utils") function runToolsTransformation({ tools, generatorStream, @@ -4316,33 +4322,33 @@ function runToolsTransformation({ abortSignal, repairToolCall }) { - let toolResultsStreamController = null; + let toolResultsStreamController = null const toolResultsStream = new ReadableStream({ start(controller) { - toolResultsStreamController = controller; + toolResultsStreamController = controller } - }); - const activeToolCalls = {}; - const outstandingToolResults = /* @__PURE__ */ new Set(); - let canClose = false; - let finishChunk = void 0; + }) + const activeToolCalls = {} + const outstandingToolResults = /* @__PURE__ */ new Set() + let canClose = false + let finishChunk = void 0 function attemptClose() { if (canClose && outstandingToolResults.size === 0) { if (finishChunk != null) { - toolResultsStreamController.enqueue(finishChunk); + toolResultsStreamController.enqueue(finishChunk) } - toolResultsStreamController.close(); + toolResultsStreamController.close() } } const forwardStream = new TransformStream({ async transform(chunk, controller) { - const chunkType = chunk.type; + const chunkType = chunk.type switch (chunkType) { case "text-delta": case "response-metadata": case "error": { - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } case "tool-call-delta": { if (toolCallStreaming) { @@ -4351,17 +4357,17 @@ function runToolsTransformation({ type: "tool-call-streaming-start", toolCallId: chunk.toolCallId, toolName: chunk.toolName - }); - activeToolCalls[chunk.toolCallId] = true; + }) + activeToolCalls[chunk.toolCallId] = true } controller.enqueue({ type: "tool-call-delta", toolCallId: chunk.toolCallId, toolName: chunk.toolName, argsTextDelta: chunk.argsTextDelta - }); + }) } - break; + break } case "tool-call": { try { @@ -4371,12 +4377,12 @@ function runToolsTransformation({ repairToolCall, system, messages - }); - controller.enqueue(toolCall); - const tool2 = tools[toolCall.toolName]; + }) + controller.enqueue(toolCall) + const tool2 = tools[toolCall.toolName] if (tool2.execute != null) { - const toolExecutionId = (0, import_ui_utils7.generateId)(); - outstandingToolResults.add(toolExecutionId); + const toolExecutionId = (0, import_ui_utils7.generateId)() + outstandingToolResults.add(toolExecutionId) recordSpan({ name: "ai.toolCall", attributes: selectTelemetryAttributes({ @@ -4404,9 +4410,9 @@ function runToolsTransformation({ ...toolCall, type: "tool-result", result - }); - outstandingToolResults.delete(toolExecutionId); - attemptClose(); + }) + outstandingToolResults.delete(toolExecutionId) + attemptClose() try { span.setAttributes( selectTelemetryAttributes({ @@ -4417,7 +4423,7 @@ function runToolsTransformation({ } } }) - ); + ) } catch (ignored) { } }, @@ -4430,20 +4436,20 @@ function runToolsTransformation({ toolArgs: toolCall.args, cause: error }) - }); - outstandingToolResults.delete(toolExecutionId); - attemptClose(); + }) + outstandingToolResults.delete(toolExecutionId) + attemptClose() } ) - }); + }) } } catch (error) { toolResultsStreamController.enqueue({ type: "error", error - }); + }) } - break; + break } case "finish": { finishChunk = { @@ -4452,27 +4458,27 @@ function runToolsTransformation({ logprobs: chunk.logprobs, usage: calculateLanguageModelUsage(chunk.usage), experimental_providerMetadata: chunk.providerMetadata - }; - break; + } + break } default: { - const _exhaustiveCheck = chunkType; - throw new Error(`Unhandled chunk type: ${_exhaustiveCheck}`); + const _exhaustiveCheck = chunkType + throw new Error(`Unhandled chunk type: ${_exhaustiveCheck}`) } } }, flush() { - canClose = true; - attemptClose(); + canClose = true + attemptClose() } - }); + }) return new ReadableStream({ async start(controller) { return Promise.all([ generatorStream.pipeThrough(forwardStream).pipeTo( new WritableStream({ write(chunk) { - controller.enqueue(chunk); + controller.enqueue(chunk) }, close() { } @@ -4481,27 +4487,27 @@ function runToolsTransformation({ toolResultsStream.pipeTo( new WritableStream({ write(chunk) { - controller.enqueue(chunk); + controller.enqueue(chunk) }, close() { - controller.close(); + controller.close() } }) ) - ]); + ]) } - }); + }) } // core/generate-text/stream-text.ts var originalGenerateId4 = (0, import_provider_utils11.createIdGenerator)({ prefix: "aitxt", size: 24 -}); +}) var originalGenerateMessageId2 = (0, import_provider_utils11.createIdGenerator)({ prefix: "msg", size: 24 -}); +}) function streamText({ model, tools, @@ -4559,33 +4565,33 @@ function streamText({ currentDate, generateId: generateId3, generateMessageId - }); + }) } function createOutputTransformStream(output) { if (!output) { return new TransformStream({ transform(chunk, controller) { - controller.enqueue({ part: chunk, partialOutput: void 0 }); + controller.enqueue({ part: chunk, partialOutput: void 0 }) } - }); + }) } - let text2 = ""; - let textChunk = ""; - let lastPublishedJson = ""; + let text2 = "" + let textChunk = "" + let lastPublishedJson = "" return new TransformStream({ transform(chunk, controller) { if (chunk.type !== "text-delta") { controller.enqueue({ part: chunk, partialOutput: void 0 - }); - return; + }) + return } - text2 += chunk.textDelta; - textChunk += chunk.textDelta; - const result = output.parsePartial({ text: text2 }); + text2 += chunk.textDelta + textChunk += chunk.textDelta + const result = output.parsePartial({ text: text2 }) if (result != null) { - const currentJson = JSON.stringify(result.partial); + const currentJson = JSON.stringify(result.partial) if (currentJson !== lastPublishedJson) { controller.enqueue({ part: { @@ -4593,9 +4599,9 @@ function createOutputTransformStream(output) { textDelta: textChunk }, partialOutput: result.partial - }); - lastPublishedJson = currentJson; - textChunk = ""; + }) + lastPublishedJson = currentJson + textChunk = "" } } }, @@ -4607,10 +4613,10 @@ function createOutputTransformStream(output) { textDelta: textChunk }, partialOutput: void 0 - }); + }) } } - }); + }) } var DefaultStreamTextResult = class { constructor({ @@ -4641,58 +4647,58 @@ var DefaultStreamTextResult = class { generateId: generateId3, generateMessageId }) { - this.warningsPromise = new DelayedPromise(); - this.usagePromise = new DelayedPromise(); - this.finishReasonPromise = new DelayedPromise(); - this.providerMetadataPromise = new DelayedPromise(); - this.textPromise = new DelayedPromise(); - this.toolCallsPromise = new DelayedPromise(); - this.toolResultsPromise = new DelayedPromise(); - this.requestPromise = new DelayedPromise(); - this.responsePromise = new DelayedPromise(); - this.stepsPromise = new DelayedPromise(); - var _a14; + this.warningsPromise = new DelayedPromise() + this.usagePromise = new DelayedPromise() + this.finishReasonPromise = new DelayedPromise() + this.providerMetadataPromise = new DelayedPromise() + this.textPromise = new DelayedPromise() + this.toolCallsPromise = new DelayedPromise() + this.toolResultsPromise = new DelayedPromise() + this.requestPromise = new DelayedPromise() + this.responsePromise = new DelayedPromise() + this.stepsPromise = new DelayedPromise() + var _a14 if (maxSteps < 1) { throw new InvalidArgumentError({ parameter: "maxSteps", value: maxSteps, message: "maxSteps must be at least 1" - }); + }) } - this.output = output; - let recordedStepText = ""; - let recordedContinuationText = ""; - let recordedFullText = ""; + this.output = output + let recordedStepText = "" + let recordedContinuationText = "" + let recordedFullText = "" const recordedResponse = { id: generateId3(), timestamp: currentDate(), modelId: model.modelId, messages: [] - }; - let recordedToolCalls = []; - let recordedToolResults = []; - let recordedFinishReason = void 0; - let recordedUsage = void 0; - let stepType = "initial"; - const recordedSteps = []; - let rootSpan; + } + let recordedToolCalls = [] + let recordedToolResults = [] + let recordedFinishReason = void 0 + let recordedUsage = void 0 + let stepType = "initial" + const recordedSteps = [] + let rootSpan const eventProcessor = new TransformStream({ async transform(chunk, controller) { - controller.enqueue(chunk); - const { part } = chunk; + controller.enqueue(chunk) + const { part } = chunk if (part.type === "text-delta" || part.type === "tool-call" || part.type === "tool-result" || part.type === "tool-call-streaming-start" || part.type === "tool-call-delta") { - await (onChunk == null ? void 0 : onChunk({ chunk: part })); + await (onChunk == null ? void 0 : onChunk({ chunk: part })) } if (part.type === "text-delta") { - recordedStepText += part.textDelta; - recordedContinuationText += part.textDelta; - recordedFullText += part.textDelta; + recordedStepText += part.textDelta + recordedContinuationText += part.textDelta + recordedFullText += part.textDelta } if (part.type === "tool-call") { - recordedToolCalls.push(part); + recordedToolCalls.push(part) } if (part.type === "tool-result") { - recordedToolResults.push(part); + recordedToolResults.push(part) } if (part.type === "step-finish") { const stepMessages = toResponseMessages({ @@ -4702,19 +4708,19 @@ var DefaultStreamTextResult = class { toolResults: recordedToolResults, messageId: part.messageId, generateMessageId - }); - const currentStep = recordedSteps.length; - let nextStepType = "done"; + }) + const currentStep = recordedSteps.length + let nextStepType = "done" if (currentStep + 1 < maxSteps) { if (continueSteps && part.finishReason === "length" && // only use continue when there are no tool calls: recordedToolCalls.length === 0) { - nextStepType = "continue"; + nextStepType = "continue" } else if ( // there are tool calls: recordedToolCalls.length > 0 && // all current tool calls have results: recordedToolResults.length === recordedToolCalls.length ) { - nextStepType = "tool-result"; + nextStepType = "tool-result" } } const currentStepResult = { @@ -4733,54 +4739,54 @@ var DefaultStreamTextResult = class { }, experimental_providerMetadata: part.experimental_providerMetadata, isContinued: part.isContinued - }; - await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)); - recordedSteps.push(currentStepResult); - recordedToolCalls = []; - recordedToolResults = []; - recordedStepText = ""; + } + await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)) + recordedSteps.push(currentStepResult) + recordedToolCalls = [] + recordedToolResults = [] + recordedStepText = "" if (nextStepType !== "done") { - stepType = nextStepType; + stepType = nextStepType } if (nextStepType !== "continue") { - recordedResponse.messages.push(...stepMessages); - recordedContinuationText = ""; + recordedResponse.messages.push(...stepMessages) + recordedContinuationText = "" } } if (part.type === "finish") { - recordedResponse.id = part.response.id; - recordedResponse.timestamp = part.response.timestamp; - recordedResponse.modelId = part.response.modelId; - recordedResponse.headers = part.response.headers; - recordedUsage = part.usage; - recordedFinishReason = part.finishReason; + recordedResponse.id = part.response.id + recordedResponse.timestamp = part.response.timestamp + recordedResponse.modelId = part.response.modelId + recordedResponse.headers = part.response.headers + recordedUsage = part.usage + recordedFinishReason = part.finishReason } }, async flush(controller) { - var _a15; + var _a15 try { if (recordedSteps.length === 0) { - return; + return } - const lastStep = recordedSteps[recordedSteps.length - 1]; - self.warningsPromise.resolve(lastStep.warnings); - self.requestPromise.resolve(lastStep.request); - self.responsePromise.resolve(lastStep.response); - self.toolCallsPromise.resolve(lastStep.toolCalls); - self.toolResultsPromise.resolve(lastStep.toolResults); + const lastStep = recordedSteps[recordedSteps.length - 1] + self.warningsPromise.resolve(lastStep.warnings) + self.requestPromise.resolve(lastStep.request) + self.responsePromise.resolve(lastStep.response) + self.toolCallsPromise.resolve(lastStep.toolCalls) + self.toolResultsPromise.resolve(lastStep.toolResults) self.providerMetadataPromise.resolve( lastStep.experimental_providerMetadata - ); - const finishReason = recordedFinishReason != null ? recordedFinishReason : "unknown"; + ) + const finishReason = recordedFinishReason != null ? recordedFinishReason : "unknown" const usage = recordedUsage != null ? recordedUsage : { completionTokens: NaN, promptTokens: NaN, totalTokens: NaN - }; - self.finishReasonPromise.resolve(finishReason); - self.usagePromise.resolve(usage); - self.textPromise.resolve(recordedFullText); - self.stepsPromise.resolve(recordedSteps); + } + self.finishReasonPromise.resolve(finishReason) + self.usagePromise.resolve(usage) + self.textPromise.resolve(recordedFullText) + self.stepsPromise.resolve(recordedSteps) await (onFinish == null ? void 0 : onFinish({ finishReason, logprobs: void 0, @@ -4793,7 +4799,7 @@ var DefaultStreamTextResult = class { warnings: lastStep.warnings, experimental_providerMetadata: lastStep.experimental_providerMetadata, steps: recordedSteps - })); + })) rootSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -4802,47 +4808,47 @@ var DefaultStreamTextResult = class { "ai.response.text": { output: () => recordedFullText }, "ai.response.toolCalls": { output: () => { - var _a16; - return ((_a16 = lastStep.toolCalls) == null ? void 0 : _a16.length) ? JSON.stringify(lastStep.toolCalls) : void 0; + var _a16 + return ((_a16 = lastStep.toolCalls) == null ? void 0 : _a16.length) ? JSON.stringify(lastStep.toolCalls) : void 0 } }, "ai.usage.promptTokens": usage.promptTokens, "ai.usage.completionTokens": usage.completionTokens } }) - ); + ) } catch (error) { - controller.error(error); + controller.error(error) } finally { - rootSpan.end(); + rootSpan.end() } } - }); - const stitchableStream = createStitchableStream(); - this.addStream = stitchableStream.addStream; - this.closeStream = stitchableStream.close; - let stream = stitchableStream.stream; + }) + const stitchableStream = createStitchableStream() + this.addStream = stitchableStream.addStream + this.closeStream = stitchableStream.close + let stream = stitchableStream.stream for (const transform of transforms) { stream = stream.pipeThrough( transform({ tools, stopStream() { - stitchableStream.terminate(); + stitchableStream.terminate() } }) - ); + ) } - this.baseStream = stream.pipeThrough(createOutputTransformStream(output)).pipeThrough(eventProcessor); + this.baseStream = stream.pipeThrough(createOutputTransformStream(output)).pipeThrough(eventProcessor) const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); + }) const initialPrompt = standardizePrompt({ prompt: { system: (_a14 = output == null ? void 0 : output.injectIntoSystemPrompt({ system, model })) != null ? _a14 : system, @@ -4850,8 +4856,8 @@ var DefaultStreamTextResult = class { messages }, tools - }); - const self = this; + }) + const self = this recordSpan({ name: "ai.streamText", attributes: selectTelemetryAttributes({ @@ -4869,7 +4875,7 @@ var DefaultStreamTextResult = class { tracer, endWhenDone: false, fn: async (rootSpanArg) => { - rootSpan = rootSpanArg; + rootSpan = rootSpanArg async function streamStep({ currentStep, responseMessages, @@ -4879,11 +4885,11 @@ var DefaultStreamTextResult = class { hasLeadingWhitespace, messageId }) { - const promptFormat = responseMessages.length === 0 ? initialPrompt.type : "messages"; + const promptFormat = responseMessages.length === 0 ? initialPrompt.type : "messages" const stepInputMessages = [ ...initialPrompt.messages, ...responseMessages - ]; + ] const promptMessages = await convertToLanguageModelPrompt({ prompt: { type: promptFormat, @@ -4892,11 +4898,11 @@ var DefaultStreamTextResult = class { }, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); + }) const mode = { type: "regular", ...prepareToolsAndToolChoice({ tools, toolChoice, activeTools }) - }; + } const { result: { stream: stream2, warnings, rawResponse, request }, doStreamSpan, @@ -4921,8 +4927,8 @@ var DefaultStreamTextResult = class { "ai.prompt.tools": { // convert the language model level tools: input: () => { - var _a15; - return (_a15 = mode.tools) == null ? void 0 : _a15.map((tool2) => JSON.stringify(tool2)); + var _a15 + return (_a15 = mode.tools) == null ? void 0 : _a15.map((tool2) => JSON.stringify(tool2)) } }, "ai.prompt.toolChoice": { @@ -4958,7 +4964,7 @@ var DefaultStreamTextResult = class { }) }) }) - ); + ) const transformedStream = runToolsTransformation({ tools, generatorStream: stream2, @@ -4969,151 +4975,151 @@ var DefaultStreamTextResult = class { messages: stepInputMessages, repairToolCall, abortSignal - }); - const stepRequest = request != null ? request : {}; - const stepToolCalls = []; - const stepToolResults = []; - let stepFinishReason = "unknown"; + }) + const stepRequest = request != null ? request : {} + const stepToolCalls = [] + const stepToolResults = [] + let stepFinishReason = "unknown" let stepUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 - }; - let stepProviderMetadata; - let stepFirstChunk = true; - let stepText = ""; - let fullStepText = stepType2 === "continue" ? previousStepText : ""; - let stepLogProbs; + } + let stepProviderMetadata + let stepFirstChunk = true + let stepText = "" + let fullStepText = stepType2 === "continue" ? previousStepText : "" + let stepLogProbs let stepResponse = { id: generateId3(), timestamp: currentDate(), modelId: model.modelId - }; - let chunkBuffer = ""; - let chunkTextPublished = false; - let inWhitespacePrefix = true; - let hasWhitespaceSuffix = false; + } + let chunkBuffer = "" + let chunkTextPublished = false + let inWhitespacePrefix = true + let hasWhitespaceSuffix = false async function publishTextChunk({ controller, chunk }) { - controller.enqueue(chunk); - stepText += chunk.textDelta; - fullStepText += chunk.textDelta; - chunkTextPublished = true; - hasWhitespaceSuffix = chunk.textDelta.trimEnd() !== chunk.textDelta; + controller.enqueue(chunk) + stepText += chunk.textDelta + fullStepText += chunk.textDelta + chunkTextPublished = true + hasWhitespaceSuffix = chunk.textDelta.trimEnd() !== chunk.textDelta } self.addStream( transformedStream.pipeThrough( new TransformStream({ async transform(chunk, controller) { - var _a15, _b, _c; + var _a15, _b, _c if (stepFirstChunk) { - const msToFirstChunk = now2() - startTimestampMs; - stepFirstChunk = false; + const msToFirstChunk = now2() - startTimestampMs + stepFirstChunk = false doStreamSpan.addEvent("ai.stream.firstChunk", { "ai.response.msToFirstChunk": msToFirstChunk - }); + }) doStreamSpan.setAttributes({ "ai.response.msToFirstChunk": msToFirstChunk - }); + }) controller.enqueue({ type: "step-start", messageId, request: stepRequest, warnings: warnings != null ? warnings : [] - }); + }) } if (chunk.type === "text-delta" && chunk.textDelta.length === 0) { - return; + return } - const chunkType = chunk.type; + const chunkType = chunk.type switch (chunkType) { case "text-delta": { if (continueSteps) { - const trimmedChunkText = inWhitespacePrefix && hasLeadingWhitespace ? chunk.textDelta.trimStart() : chunk.textDelta; + const trimmedChunkText = inWhitespacePrefix && hasLeadingWhitespace ? chunk.textDelta.trimStart() : chunk.textDelta if (trimmedChunkText.length === 0) { - break; + break } - inWhitespacePrefix = false; - chunkBuffer += trimmedChunkText; - const split = splitOnLastWhitespace(chunkBuffer); + inWhitespacePrefix = false + chunkBuffer += trimmedChunkText + const split = splitOnLastWhitespace(chunkBuffer) if (split != null) { - chunkBuffer = split.suffix; + chunkBuffer = split.suffix await publishTextChunk({ controller, chunk: { type: "text-delta", textDelta: split.prefix + split.whitespace } - }); + }) } } else { - await publishTextChunk({ controller, chunk }); + await publishTextChunk({ controller, chunk }) } - break; + break } case "tool-call": { - controller.enqueue(chunk); - stepToolCalls.push(chunk); - break; + controller.enqueue(chunk) + stepToolCalls.push(chunk) + break } case "tool-result": { - controller.enqueue(chunk); - stepToolResults.push(chunk); - break; + controller.enqueue(chunk) + stepToolResults.push(chunk) + break } case "response-metadata": { stepResponse = { id: (_a15 = chunk.id) != null ? _a15 : stepResponse.id, timestamp: (_b = chunk.timestamp) != null ? _b : stepResponse.timestamp, modelId: (_c = chunk.modelId) != null ? _c : stepResponse.modelId - }; - break; + } + break } case "finish": { - stepUsage = chunk.usage; - stepFinishReason = chunk.finishReason; - stepProviderMetadata = chunk.experimental_providerMetadata; - stepLogProbs = chunk.logprobs; - const msToFinish = now2() - startTimestampMs; - doStreamSpan.addEvent("ai.stream.finish"); + stepUsage = chunk.usage + stepFinishReason = chunk.finishReason + stepProviderMetadata = chunk.experimental_providerMetadata + stepLogProbs = chunk.logprobs + const msToFinish = now2() - startTimestampMs + doStreamSpan.addEvent("ai.stream.finish") doStreamSpan.setAttributes({ "ai.response.msToFinish": msToFinish, "ai.response.avgCompletionTokensPerSecond": 1e3 * stepUsage.completionTokens / msToFinish - }); - break; + }) + break } case "tool-call-streaming-start": case "tool-call-delta": { - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } case "error": { - controller.enqueue(chunk); - stepFinishReason = "error"; - break; + controller.enqueue(chunk) + stepFinishReason = "error" + break } default: { - const exhaustiveCheck = chunkType; - throw new Error(`Unknown chunk type: ${exhaustiveCheck}`); + const exhaustiveCheck = chunkType + throw new Error(`Unknown chunk type: ${exhaustiveCheck}`) } } }, // invoke onFinish callback and resolve toolResults promise when the stream is about to close: async flush(controller) { - const stepToolCallsJson = stepToolCalls.length > 0 ? JSON.stringify(stepToolCalls) : void 0; - let nextStepType = "done"; + const stepToolCallsJson = stepToolCalls.length > 0 ? JSON.stringify(stepToolCalls) : void 0 + let nextStepType = "done" if (currentStep + 1 < maxSteps) { if (continueSteps && stepFinishReason === "length" && // only use continue when there are no tool calls: stepToolCalls.length === 0) { - nextStepType = "continue"; + nextStepType = "continue" } else if ( // there are tool calls: stepToolCalls.length > 0 && // all current tool calls have results: stepToolResults.length === stepToolCalls.length ) { - nextStepType = "tool-result"; + nextStepType = "tool-result" } } if (continueSteps && chunkBuffer.length > 0 && (nextStepType !== "continue" || // when the next step is a regular step, publish the buffer @@ -5124,8 +5130,8 @@ var DefaultStreamTextResult = class { type: "text-delta", textDelta: chunkBuffer } - }); - chunkBuffer = ""; + }) + chunkBuffer = "" } try { doStreamSpan.setAttributes( @@ -5150,10 +5156,10 @@ var DefaultStreamTextResult = class { "gen_ai.usage.output_tokens": stepUsage.completionTokens } }) - ); + ) } catch (error) { } finally { - doStreamSpan.end(); + doStreamSpan.end() } controller.enqueue({ type: "step-finish", @@ -5169,8 +5175,8 @@ var DefaultStreamTextResult = class { warnings, isContinued: nextStepType === "continue", messageId - }); - const combinedUsage = addLanguageModelUsage(usage, stepUsage); + }) + const combinedUsage = addLanguageModelUsage(usage, stepUsage) if (nextStepType === "done") { controller.enqueue({ type: "finish", @@ -5182,18 +5188,18 @@ var DefaultStreamTextResult = class { ...stepResponse, headers: rawResponse == null ? void 0 : rawResponse.headers } - }); - self.closeStream(); + }) + self.closeStream() } else { if (stepType2 === "continue") { - const lastMessage = responseMessages[responseMessages.length - 1]; + const lastMessage = responseMessages[responseMessages.length - 1] if (typeof lastMessage.content === "string") { - lastMessage.content += stepText; + lastMessage.content += stepText } else { lastMessage.content.push({ text: stepText, type: "text" - }); + }) } } else { responseMessages.push( @@ -5205,7 +5211,7 @@ var DefaultStreamTextResult = class { messageId, generateMessageId }) - ); + ) } await streamStep({ currentStep: currentStep + 1, @@ -5218,12 +5224,12 @@ var DefaultStreamTextResult = class { // keep the same id when continuing a step: nextStepType === "continue" ? messageId : generateMessageId() ) - }); + }) } } }) ) - ); + ) } await streamStep({ currentStep: 0, @@ -5237,49 +5243,49 @@ var DefaultStreamTextResult = class { stepType: "initial", hasLeadingWhitespace: false, messageId: generateMessageId() - }); + }) } }).catch((error) => { self.addStream( new ReadableStream({ start(controller) { - controller.enqueue({ type: "error", error }); - controller.close(); + controller.enqueue({ type: "error", error }) + controller.close() } }) - ); - self.closeStream(); - }); + ) + self.closeStream() + }) } get warnings() { - return this.warningsPromise.value; + return this.warningsPromise.value } get usage() { - return this.usagePromise.value; + return this.usagePromise.value } get finishReason() { - return this.finishReasonPromise.value; + return this.finishReasonPromise.value } get experimental_providerMetadata() { - return this.providerMetadataPromise.value; + return this.providerMetadataPromise.value } get text() { - return this.textPromise.value; + return this.textPromise.value } get toolCalls() { - return this.toolCallsPromise.value; + return this.toolCallsPromise.value } get toolResults() { - return this.toolResultsPromise.value; + return this.toolResultsPromise.value } get request() { - return this.requestPromise.value; + return this.requestPromise.value } get response() { - return this.responsePromise.value; + return this.responsePromise.value } get steps() { - return this.stepsPromise.value; + return this.stepsPromise.value } /** Split out a new stream from the original stream. @@ -5290,9 +5296,9 @@ var DefaultStreamTextResult = class { However, the LLM results are expected to be small enough to not cause issues. */ teeStream() { - const [stream1, stream2] = this.baseStream.tee(); - this.baseStream = stream2; - return stream1; + const [stream1, stream2] = this.baseStream.tee() + this.baseStream = stream2 + return stream1 } get textStream() { return createAsyncIterableStream( @@ -5300,63 +5306,63 @@ var DefaultStreamTextResult = class { new TransformStream({ transform({ part }, controller) { if (part.type === "text-delta") { - controller.enqueue(part.textDelta); + controller.enqueue(part.textDelta) } else if (part.type === "error") { - controller.error(part.error); + controller.error(part.error) } } }) ) - ); + ) } get fullStream() { return createAsyncIterableStream( this.teeStream().pipeThrough( new TransformStream({ transform({ part }, controller) { - controller.enqueue(part); + controller.enqueue(part) } }) ) - ); + ) } get experimental_partialOutputStream() { if (this.output == null) { - throw new NoOutputSpecifiedError(); + throw new NoOutputSpecifiedError() } return createAsyncIterableStream( this.teeStream().pipeThrough( new TransformStream({ transform({ partialOutput }, controller) { if (partialOutput != null) { - controller.enqueue(partialOutput); + controller.enqueue(partialOutput) } } }) ) - ); + ) } toDataStreamInternal({ getErrorMessage: getErrorMessage5 = () => "An error occurred.", // mask error messages for safety by default sendUsage = true } = {}) { - let aggregatedResponse = ""; + let aggregatedResponse = "" const callbackTransformer = new TransformStream({ async transform(chunk, controller) { - controller.enqueue(chunk); + controller.enqueue(chunk) if (chunk.type === "text-delta") { - aggregatedResponse += chunk.textDelta; + aggregatedResponse += chunk.textDelta } } - }); + }) const streamPartsTransformer = new TransformStream({ transform: async (chunk, controller) => { - const chunkType = chunk.type; + const chunkType = chunk.type switch (chunkType) { case "text-delta": { - controller.enqueue((0, import_ui_utils8.formatDataStreamPart)("text", chunk.textDelta)); - break; + controller.enqueue((0, import_ui_utils8.formatDataStreamPart)("text", chunk.textDelta)) + break } case "tool-call-streaming-start": { controller.enqueue( @@ -5364,8 +5370,8 @@ var DefaultStreamTextResult = class { toolCallId: chunk.toolCallId, toolName: chunk.toolName }) - ); - break; + ) + break } case "tool-call-delta": { controller.enqueue( @@ -5373,8 +5379,8 @@ var DefaultStreamTextResult = class { toolCallId: chunk.toolCallId, argsTextDelta: chunk.argsTextDelta }) - ); - break; + ) + break } case "tool-call": { controller.enqueue( @@ -5383,8 +5389,8 @@ var DefaultStreamTextResult = class { toolName: chunk.toolName, args: chunk.args }) - ); - break; + ) + break } case "tool-result": { controller.enqueue( @@ -5392,22 +5398,22 @@ var DefaultStreamTextResult = class { toolCallId: chunk.toolCallId, result: chunk.result }) - ); - break; + ) + break } case "error": { controller.enqueue( (0, import_ui_utils8.formatDataStreamPart)("error", getErrorMessage5(chunk.error)) - ); - break; + ) + break } case "step-start": { controller.enqueue( (0, import_ui_utils8.formatDataStreamPart)("start_step", { messageId: chunk.messageId }) - ); - break; + ) + break } case "step-finish": { controller.enqueue( @@ -5419,8 +5425,8 @@ var DefaultStreamTextResult = class { } : void 0, isContinued: chunk.isContinued }) - ); - break; + ) + break } case "finish": { controller.enqueue( @@ -5431,17 +5437,17 @@ var DefaultStreamTextResult = class { completionTokens: chunk.usage.completionTokens } : void 0 }) - ); - break; + ) + break } default: { - const exhaustiveCheck = chunkType; - throw new Error(`Unknown chunk type: ${exhaustiveCheck}`); + const exhaustiveCheck = chunkType + throw new Error(`Unknown chunk type: ${exhaustiveCheck}`) } } } - }); - return this.fullStream.pipeThrough(callbackTransformer).pipeThrough(streamPartsTransformer); + }) + return this.fullStream.pipeThrough(callbackTransformer).pipeThrough(streamPartsTransformer) } pipeDataStreamToResponse(response, { status, @@ -5460,7 +5466,7 @@ var DefaultStreamTextResult = class { dataStreamVersion: "v1" }), stream: this.toDataStream({ data, getErrorMessage: getErrorMessage5, sendUsage }) - }); + }) } pipeTextStreamToResponse(response, init) { writeToServerResponse({ @@ -5471,22 +5477,22 @@ var DefaultStreamTextResult = class { contentType: "text/plain; charset=utf-8" }), stream: this.textStream.pipeThrough(new TextEncoderStream()) - }); + }) } // TODO breaking change 5.0: remove pipeThrough(new TextEncoderStream()) toDataStream(options) { const stream = this.toDataStreamInternal({ getErrorMessage: options == null ? void 0 : options.getErrorMessage, sendUsage: options == null ? void 0 : options.sendUsage - }).pipeThrough(new TextEncoderStream()); - return (options == null ? void 0 : options.data) ? mergeStreams(options == null ? void 0 : options.data.stream, stream) : stream; + }).pipeThrough(new TextEncoderStream()) + return (options == null ? void 0 : options.data) ? mergeStreams(options == null ? void 0 : options.data.stream, stream) : stream } mergeIntoDataStream(writer) { writer.merge( this.toDataStreamInternal({ getErrorMessage: writer.onError }) - ); + ) } toDataStreamResponse({ headers, @@ -5506,18 +5512,18 @@ var DefaultStreamTextResult = class { dataStreamVersion: "v1" }) } - ); + ) } toTextStreamResponse(init) { - var _a14; + var _a14 return new Response(this.textStream.pipeThrough(new TextEncoderStream()), { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, headers: prepareResponseHeaders(init == null ? void 0 : init.headers, { contentType: "text/plain; charset=utf-8" }) - }); + }) } -}; +} // core/middleware/wrap-language-model.ts var experimental_wrapLanguageModel = ({ @@ -5530,7 +5536,7 @@ var experimental_wrapLanguageModel = ({ params, type }) { - return transformParams ? await transformParams({ params, type }) : params; + return transformParams ? await transformParams({ params, type }) : params } return { specificationVersion: "v1", @@ -5541,27 +5547,27 @@ var experimental_wrapLanguageModel = ({ supportsUrl: model.supportsUrl, supportsStructuredOutputs: model.supportsStructuredOutputs, async doGenerate(params) { - const transformedParams = await doTransform({ params, type: "generate" }); - const doGenerate = async () => model.doGenerate(transformedParams); - return wrapGenerate ? wrapGenerate({ doGenerate, params: transformedParams, model }) : doGenerate(); + const transformedParams = await doTransform({ params, type: "generate" }) + const doGenerate = async () => model.doGenerate(transformedParams) + return wrapGenerate ? wrapGenerate({ doGenerate, params: transformedParams, model }) : doGenerate() }, async doStream(params) { - const transformedParams = await doTransform({ params, type: "stream" }); - const doStream = async () => model.doStream(transformedParams); - return wrapStream ? wrapStream({ doStream, params: transformedParams, model }) : doStream(); + const transformedParams = await doTransform({ params, type: "stream" }) + const doStream = async () => model.doStream(transformedParams) + return wrapStream ? wrapStream({ doStream, params: transformedParams, model }) : doStream() } - }; -}; + } +} // core/prompt/append-response-messages.ts function appendResponseMessages({ messages, responseMessages }) { - var _a14; - const clonedMessages = structuredClone(messages); + var _a14 + const clonedMessages = structuredClone(messages) for (const message of responseMessages) { - const role = message.role; + const role = message.role switch (role) { case "assistant": { clonedMessages.push({ @@ -5576,41 +5582,41 @@ function appendResponseMessages({ state: "call", ...call })) - }); - break; + }) + break } case "tool": { const previousMessage = clonedMessages[clonedMessages.length - 1]; - (_a14 = previousMessage.toolInvocations) != null ? _a14 : previousMessage.toolInvocations = []; + (_a14 = previousMessage.toolInvocations) != null ? _a14 : previousMessage.toolInvocations = [] if (previousMessage.role !== "assistant") { throw new Error( `Tool result must follow an assistant message: ${previousMessage.role}` - ); + ) } for (const part of message.content) { const toolCall = previousMessage.toolInvocations.find( (call) => call.toolCallId === part.toolCallId - ); + ) if (!toolCall) { - throw new Error("Tool call not found in previous message"); + throw new Error("Tool call not found in previous message") } - toolCall.state = "result"; - const toolResult = toolCall; - toolResult.result = part.result; + toolCall.state = "result" + const toolResult = toolCall + toolResult.result = part.result } - break; + break } default: { - const _exhaustiveCheck = role; - throw new Error(`Unsupported message role: ${_exhaustiveCheck}`); + const _exhaustiveCheck = role + throw new Error(`Unsupported message role: ${_exhaustiveCheck}`) } } } - return clonedMessages; + return clonedMessages } // core/registry/custom-provider.ts -var import_provider18 = require("@ai-sdk/provider"); +var import_provider18 = require("@ai-sdk/provider") function experimental_customProvider({ languageModels, textEmbeddingModels, @@ -5619,31 +5625,31 @@ function experimental_customProvider({ return { languageModel(modelId) { if (languageModels != null && modelId in languageModels) { - return languageModels[modelId]; + return languageModels[modelId] } if (fallbackProvider) { - return fallbackProvider.languageModel(modelId); + return fallbackProvider.languageModel(modelId) } - throw new import_provider18.NoSuchModelError({ modelId, modelType: "languageModel" }); + throw new import_provider18.NoSuchModelError({ modelId, modelType: "languageModel" }) }, textEmbeddingModel(modelId) { if (textEmbeddingModels != null && modelId in textEmbeddingModels) { - return textEmbeddingModels[modelId]; + return textEmbeddingModels[modelId] } if (fallbackProvider) { - return fallbackProvider.textEmbeddingModel(modelId); + return fallbackProvider.textEmbeddingModel(modelId) } - throw new import_provider18.NoSuchModelError({ modelId, modelType: "textEmbeddingModel" }); + throw new import_provider18.NoSuchModelError({ modelId, modelType: "textEmbeddingModel" }) } - }; + } } // core/registry/no-such-provider-error.ts -var import_provider19 = require("@ai-sdk/provider"); -var name13 = "AI_NoSuchProviderError"; -var marker13 = `vercel.ai.error.${name13}`; -var symbol13 = Symbol.for(marker13); -var _a13; +var import_provider19 = require("@ai-sdk/provider") +var name13 = "AI_NoSuchProviderError" +var marker13 = `vercel.ai.error.${name13}` +var symbol13 = Symbol.for(marker13) +var _a13 var NoSuchProviderError = class extends import_provider19.NoSuchModelError { constructor({ modelId, @@ -5652,123 +5658,123 @@ var NoSuchProviderError = class extends import_provider19.NoSuchModelError { availableProviders, message = `No such provider: ${providerId} (available providers: ${availableProviders.join()})` }) { - super({ errorName: name13, modelId, modelType, message }); - this[_a13] = true; - this.providerId = providerId; - this.availableProviders = availableProviders; + super({ errorName: name13, modelId, modelType, message }) + this[_a13] = true + this.providerId = providerId + this.availableProviders = availableProviders } static isInstance(error) { - return import_provider19.AISDKError.hasMarker(error, marker13); + return import_provider19.AISDKError.hasMarker(error, marker13) } -}; -_a13 = symbol13; +} +_a13 = symbol13 // core/registry/provider-registry.ts -var import_provider20 = require("@ai-sdk/provider"); +var import_provider20 = require("@ai-sdk/provider") function experimental_createProviderRegistry(providers) { - const registry = new DefaultProviderRegistry(); + const registry = new DefaultProviderRegistry() for (const [id, provider] of Object.entries(providers)) { - registry.registerProvider({ id, provider }); + registry.registerProvider({ id, provider }) } - return registry; + return registry } var DefaultProviderRegistry = class { constructor() { - this.providers = {}; + this.providers = {} } registerProvider({ id, provider }) { - this.providers[id] = provider; + this.providers[id] = provider } getProvider(id) { - const provider = this.providers[id]; + const provider = this.providers[id] if (provider == null) { throw new NoSuchProviderError({ modelId: id, modelType: "languageModel", providerId: id, availableProviders: Object.keys(this.providers) - }); + }) } - return provider; + return provider } splitId(id, modelType) { - const index = id.indexOf(":"); + const index = id.indexOf(":") if (index === -1) { throw new import_provider20.NoSuchModelError({ modelId: id, modelType, message: `Invalid ${modelType} id for registry: ${id} (must be in the format "providerId:modelId")` - }); + }) } - return [id.slice(0, index), id.slice(index + 1)]; + return [id.slice(0, index), id.slice(index + 1)] } languageModel(id) { - var _a14, _b; - const [providerId, modelId] = this.splitId(id, "languageModel"); - const model = (_b = (_a14 = this.getProvider(providerId)).languageModel) == null ? void 0 : _b.call(_a14, modelId); + var _a14, _b + const [providerId, modelId] = this.splitId(id, "languageModel") + const model = (_b = (_a14 = this.getProvider(providerId)).languageModel) == null ? void 0 : _b.call(_a14, modelId) if (model == null) { - throw new import_provider20.NoSuchModelError({ modelId: id, modelType: "languageModel" }); + throw new import_provider20.NoSuchModelError({ modelId: id, modelType: "languageModel" }) } - return model; + return model } textEmbeddingModel(id) { - var _a14; - const [providerId, modelId] = this.splitId(id, "textEmbeddingModel"); - const provider = this.getProvider(providerId); - const model = (_a14 = provider.textEmbeddingModel) == null ? void 0 : _a14.call(provider, modelId); + var _a14 + const [providerId, modelId] = this.splitId(id, "textEmbeddingModel") + const provider = this.getProvider(providerId) + const model = (_a14 = provider.textEmbeddingModel) == null ? void 0 : _a14.call(provider, modelId) if (model == null) { throw new import_provider20.NoSuchModelError({ modelId: id, modelType: "textEmbeddingModel" - }); + }) } - return model; + return model } /** * @deprecated Use `textEmbeddingModel` instead. */ textEmbedding(id) { - return this.textEmbeddingModel(id); + return this.textEmbeddingModel(id) } -}; +} // core/tool/tool.ts function tool(tool2) { - return tool2; + return tool2 } // core/util/cosine-similarity.ts function cosineSimilarity(vector1, vector2, options = { throwErrorForEmptyVectors: false }) { - const { throwErrorForEmptyVectors } = options; + const { throwErrorForEmptyVectors } = options if (vector1.length !== vector2.length) { throw new Error( `Vectors must have the same length (vector1: ${vector1.length} elements, vector2: ${vector2.length} elements)` - ); + ) } if (throwErrorForEmptyVectors && vector1.length === 0) { throw new InvalidArgumentError({ parameter: "vector1", value: vector1, message: "Vectors cannot be empty" - }); + }) } - const magnitude1 = magnitude(vector1); - const magnitude2 = magnitude(vector2); + const magnitude1 = magnitude(vector1) + const magnitude2 = magnitude(vector2) if (magnitude1 === 0 || magnitude2 === 0) { - return 0; + return 0 } - return dotProduct(vector1, vector2) / (magnitude1 * magnitude2); + return dotProduct(vector1, vector2) / (magnitude1 * magnitude2) } function dotProduct(vector1, vector2) { return vector1.reduce( (accumulator, value, index) => accumulator + value * vector2[index], 0 - ); + ) } function magnitude(vector) { - return Math.sqrt(dotProduct(vector, vector)); + return Math.sqrt(dotProduct(vector, vector)) } // core/util/simulate-readable-stream.ts @@ -5778,50 +5784,50 @@ function simulateReadableStream({ chunkDelayInMs = 0, _internal }) { - var _a14; - const delay2 = (_a14 = _internal == null ? void 0 : _internal.delay) != null ? _a14 : delay; - let index = 0; + var _a14 + const delay2 = (_a14 = _internal == null ? void 0 : _internal.delay) != null ? _a14 : delay + let index = 0 return new ReadableStream({ async pull(controller) { if (index < chunks.length) { - await delay2(index === 0 ? initialDelayInMs : chunkDelayInMs); - controller.enqueue(chunks[index++]); + await delay2(index === 0 ? initialDelayInMs : chunkDelayInMs) + controller.enqueue(chunks[index++]) } else { - controller.close(); + controller.close() } } - }); + }) } // streams/assistant-response.ts -var import_ui_utils10 = require("@ai-sdk/ui-utils"); +var import_ui_utils10 = require("@ai-sdk/ui-utils") function AssistantResponse({ threadId, messageId }, process2) { const stream = new ReadableStream({ async start(controller) { - var _a14; - const textEncoder = new TextEncoder(); + var _a14 + const textEncoder = new TextEncoder() const sendMessage = (message) => { controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("assistant_message", message) ) - ); - }; + ) + } const sendDataMessage = (message) => { controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("data_message", message) ) - ); - }; + ) + } const sendError = (errorMessage) => { controller.enqueue( textEncoder.encode((0, import_ui_utils10.formatAssistantStreamPart)("error", errorMessage)) - ); - }; + ) + } const forwardStream = async (stream2) => { - var _a15, _b; - let result = void 0; + var _a15, _b + let result = void 0 for await (const value of stream2) { switch (value.event) { case "thread.message.created": { @@ -5833,29 +5839,29 @@ function AssistantResponse({ threadId, messageId }, process2) { content: [{ type: "text", text: { value: "" } }] }) ) - ); - break; + ) + break } case "thread.message.delta": { - const content = (_a15 = value.data.delta.content) == null ? void 0 : _a15[0]; + const content = (_a15 = value.data.delta.content) == null ? void 0 : _a15[0] if ((content == null ? void 0 : content.type) === "text" && ((_b = content.text) == null ? void 0 : _b.value) != null) { controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("text", content.text.value) ) - ); + ) } - break; + break } case "thread.run.completed": case "thread.run.requires_action": { - result = value.data; - break; + result = value.data + break } } } - return result; - }; + return result + } controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("assistant_control_data", { @@ -5863,68 +5869,68 @@ function AssistantResponse({ threadId, messageId }, process2) { messageId }) ) - ); + ) try { await process2({ sendMessage, sendDataMessage, forwardStream - }); + }) } catch (error) { - sendError((_a14 = error.message) != null ? _a14 : `${error}`); + sendError((_a14 = error.message) != null ? _a14 : `${error}`) } finally { - controller.close(); + controller.close() } }, pull(controller) { }, cancel() { } - }); + }) return new Response(stream, { status: 200, headers: { "Content-Type": "text/plain; charset=utf-8" } - }); + }) } // streams/langchain-adapter.ts -var langchain_adapter_exports = {}; +var langchain_adapter_exports = {} __export(langchain_adapter_exports, { mergeIntoDataStream: () => mergeIntoDataStream, toDataStream: () => toDataStream, toDataStreamResponse: () => toDataStreamResponse -}); -var import_ui_utils11 = require("@ai-sdk/ui-utils"); +}) +var import_ui_utils11 = require("@ai-sdk/ui-utils") // streams/stream-callbacks.ts function createCallbacksTransformer(callbacks = {}) { - const textEncoder = new TextEncoder(); - let aggregatedResponse = ""; + const textEncoder = new TextEncoder() + let aggregatedResponse = "" return new TransformStream({ async start() { if (callbacks.onStart) - await callbacks.onStart(); + await callbacks.onStart() }, async transform(message, controller) { - controller.enqueue(textEncoder.encode(message)); - aggregatedResponse += message; + controller.enqueue(textEncoder.encode(message)) + aggregatedResponse += message if (callbacks.onToken) - await callbacks.onToken(message); + await callbacks.onToken(message) if (callbacks.onText && typeof message === "string") { - await callbacks.onText(message); + await callbacks.onText(message) } }, async flush() { if (callbacks.onCompletion) { - await callbacks.onCompletion(aggregatedResponse); + await callbacks.onCompletion(aggregatedResponse) } if (callbacks.onFinal) { - await callbacks.onFinal(aggregatedResponse); + await callbacks.onFinal(aggregatedResponse) } } - }); + }) } // streams/langchain-adapter.ts @@ -5932,45 +5938,45 @@ function toDataStreamInternal(stream, callbacks) { return stream.pipeThrough( new TransformStream({ transform: async (value, controller) => { - var _a14; + var _a14 if (typeof value === "string") { - controller.enqueue(value); - return; + controller.enqueue(value) + return } if ("event" in value) { if (value.event === "on_chat_model_stream") { forwardAIMessageChunk( (_a14 = value.data) == null ? void 0 : _a14.chunk, controller - ); + ) } - return; + return } - forwardAIMessageChunk(value, controller); + forwardAIMessageChunk(value, controller) } }) ).pipeThrough(createCallbacksTransformer(callbacks)).pipeThrough(new TextDecoderStream()).pipeThrough( new TransformStream({ transform: async (chunk, controller) => { - controller.enqueue((0, import_ui_utils11.formatDataStreamPart)("text", chunk)); + controller.enqueue((0, import_ui_utils11.formatDataStreamPart)("text", chunk)) } }) - ); + ) } function toDataStream(stream, callbacks) { return toDataStreamInternal(stream, callbacks).pipeThrough( new TextEncoderStream() - ); + ) } function toDataStreamResponse(stream, options) { - var _a14; + var _a14 const dataStream = toDataStreamInternal( stream, options == null ? void 0 : options.callbacks - ).pipeThrough(new TextEncoderStream()); - const data = options == null ? void 0 : options.data; - const init = options == null ? void 0 : options.init; - const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream; + ).pipeThrough(new TextEncoderStream()) + const data = options == null ? void 0 : options.data + const init = options == null ? void 0 : options.init + const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream return new Response(responseStream, { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, statusText: init == null ? void 0 : init.statusText, @@ -5978,61 +5984,61 @@ function toDataStreamResponse(stream, options) { contentType: "text/plain; charset=utf-8", dataStreamVersion: "v1" }) - }); + }) } function mergeIntoDataStream(stream, options) { - options.dataStream.merge(toDataStreamInternal(stream, options.callbacks)); + options.dataStream.merge(toDataStreamInternal(stream, options.callbacks)) } function forwardAIMessageChunk(chunk, controller) { if (typeof chunk.content === "string") { - controller.enqueue(chunk.content); + controller.enqueue(chunk.content) } else { - const content = chunk.content; + const content = chunk.content for (const item of content) { if (item.type === "text") { - controller.enqueue(item.text); + controller.enqueue(item.text) } } } } // streams/llamaindex-adapter.ts -var llamaindex_adapter_exports = {}; +var llamaindex_adapter_exports = {} __export(llamaindex_adapter_exports, { mergeIntoDataStream: () => mergeIntoDataStream2, toDataStream: () => toDataStream2, toDataStreamResponse: () => toDataStreamResponse2 -}); -var import_provider_utils13 = require("@ai-sdk/provider-utils"); -var import_ui_utils12 = require("@ai-sdk/ui-utils"); +}) +var import_provider_utils13 = require("@ai-sdk/provider-utils") +var import_ui_utils12 = require("@ai-sdk/ui-utils") function toDataStreamInternal2(stream, callbacks) { - const trimStart = trimStartOfStream(); + const trimStart = trimStartOfStream() return (0, import_provider_utils13.convertAsyncIteratorToReadableStream)(stream[Symbol.asyncIterator]()).pipeThrough( new TransformStream({ async transform(message, controller) { - controller.enqueue(trimStart(message.delta)); + controller.enqueue(trimStart(message.delta)) } }) ).pipeThrough(createCallbacksTransformer(callbacks)).pipeThrough(new TextDecoderStream()).pipeThrough( new TransformStream({ transform: async (chunk, controller) => { - controller.enqueue((0, import_ui_utils12.formatDataStreamPart)("text", chunk)); + controller.enqueue((0, import_ui_utils12.formatDataStreamPart)("text", chunk)) } }) - ); + ) } function toDataStream2(stream, callbacks) { return toDataStreamInternal2(stream, callbacks).pipeThrough( new TextEncoderStream() - ); + ) } function toDataStreamResponse2(stream, options = {}) { - var _a14; - const { init, data, callbacks } = options; + var _a14 + const { init, data, callbacks } = options const dataStream = toDataStreamInternal2(stream, callbacks).pipeThrough( new TextEncoderStream() - ); - const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream; + ) + const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream return new Response(responseStream, { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, statusText: init == null ? void 0 : init.statusText, @@ -6040,93 +6046,93 @@ function toDataStreamResponse2(stream, options = {}) { contentType: "text/plain; charset=utf-8", dataStreamVersion: "v1" }) - }); + }) } function mergeIntoDataStream2(stream, options) { - options.dataStream.merge(toDataStreamInternal2(stream, options.callbacks)); + options.dataStream.merge(toDataStreamInternal2(stream, options.callbacks)) } function trimStartOfStream() { - let isStreamStart = true; + let isStreamStart = true return (text2) => { if (isStreamStart) { - text2 = text2.trimStart(); + text2 = text2.trimStart() if (text2) - isStreamStart = false; + isStreamStart = false } - return text2; - }; + return text2 + } } // streams/stream-data.ts -var import_ui_utils13 = require("@ai-sdk/ui-utils"); +var import_ui_utils13 = require("@ai-sdk/ui-utils") // util/constants.ts -var HANGING_STREAM_WARNING_TIME_MS = 15 * 1e3; +var HANGING_STREAM_WARNING_TIME_MS = 15 * 1e3 // streams/stream-data.ts var StreamData = class { constructor() { - this.encoder = new TextEncoder(); - this.controller = null; - this.isClosed = false; - this.warningTimeout = null; - const self = this; + this.encoder = new TextEncoder() + this.controller = null + this.isClosed = false + this.warningTimeout = null + const self = this this.stream = new ReadableStream({ start: async (controller) => { - self.controller = controller; + self.controller = controller if (process.env.NODE_ENV === "development") { self.warningTimeout = setTimeout(() => { console.warn( "The data stream is hanging. Did you forget to close it with `data.close()`?" - ); - }, HANGING_STREAM_WARNING_TIME_MS); + ) + }, HANGING_STREAM_WARNING_TIME_MS) } }, pull: (controller) => { }, cancel: (reason) => { - this.isClosed = true; + this.isClosed = true } - }); + }) } async close() { if (this.isClosed) { - throw new Error("Data Stream has already been closed."); + throw new Error("Data Stream has already been closed.") } if (!this.controller) { - throw new Error("Stream controller is not initialized."); + throw new Error("Stream controller is not initialized.") } - this.controller.close(); - this.isClosed = true; + this.controller.close() + this.isClosed = true if (this.warningTimeout) { - clearTimeout(this.warningTimeout); + clearTimeout(this.warningTimeout) } } append(value) { if (this.isClosed) { - throw new Error("Data Stream has already been closed."); + throw new Error("Data Stream has already been closed.") } if (!this.controller) { - throw new Error("Stream controller is not initialized."); + throw new Error("Stream controller is not initialized.") } this.controller.enqueue( this.encoder.encode((0, import_ui_utils13.formatDataStreamPart)("data", [value])) - ); + ) } appendMessageAnnotation(value) { if (this.isClosed) { - throw new Error("Data Stream has already been closed."); + throw new Error("Data Stream has already been closed.") } if (!this.controller) { - throw new Error("Stream controller is not initialized."); + throw new Error("Stream controller is not initialized.") } this.controller.enqueue( this.encoder.encode((0, import_ui_utils13.formatDataStreamPart)("message_annotations", [value])) - ); + ) } -}; +} // Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { +module.exports = { AISDKError, APICallError, AssistantResponse, @@ -6184,5 +6190,4 @@ var StreamData = class { streamObject, streamText, tool -}); -//# sourceMappingURL=index.js.map \ No newline at end of file +} diff --git a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js index 122b5ad..9be96ba 100644 --- a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js +++ b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js @@ -607,8 +607,7 @@ function createGigachat(options = {}) { } var gigachat = createGigachat(); // Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { +module.exports = { createGigachat, gigachat -}); -//# sourceMappingURL=index.js.map \ No newline at end of file +} diff --git a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js index 50275f0..da21202 100644 --- a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js +++ b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js @@ -84,7 +84,7 @@ router.use(async (req, res, next) => { process.env.GIGACHAT_ACCESS_TOKEN = json.access_token; process.env.GIGACHAT_EXPIRES_AT = json.expires_at; console.log(JSON.stringify(response.data)); - } catch { + } catch (error) { console.log(error); } } From 2925d0f17b59c3f1965517c9aed4c7ec4bd7a671 Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Mon, 10 Feb 2025 22:13:55 +0300 Subject: [PATCH 21/41] fix eslint --- .../kfu-m-24-1/eng-it-lean/gigachat/ai.js | 3194 +++++++++-------- .../eng-it-lean/gigachat/gigachat.js | 5 +- .../kfu-m-24-1/eng-it-lean/gigachat/index.js | 2 +- 3 files changed, 1602 insertions(+), 1599 deletions(-) diff --git a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js index 6b77ac3..00ae039 100644 --- a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js +++ b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/ai.js @@ -1,24 +1,26 @@ -"use strict"; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; +/* eslint-disable no-empty */ +/* eslint-disable no-async-promise-executor */ +"use strict" +var __defProp = Object.defineProperty +var __getOwnPropDesc = Object.getOwnPropertyDescriptor +var __getOwnPropNames = Object.getOwnPropertyNames +var __hasOwnProp = Object.prototype.hasOwnProperty var __export = (target, all) => { for (var name14 in all) - __defProp(target, name14, { get: all[name14], enumerable: true }); -}; + __defProp(target, name14, { get: all[name14], enumerable: true }) +} var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }) } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + return to +} +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod) // streams/index.ts -var streams_exports = {}; +var streams_exports = {} __export(streams_exports, { AISDKError: () => import_provider16.AISDKError, APICallError: () => import_provider16.APICallError, @@ -77,81 +79,83 @@ __export(streams_exports, { streamObject: () => streamObject, streamText: () => streamText, tool: () => tool -}); -module.exports = __toCommonJS(streams_exports); +}) +module.exports = __toCommonJS(streams_exports) // core/index.ts -var import_provider_utils12 = require("@ai-sdk/provider-utils"); -var import_ui_utils9 = require("@ai-sdk/ui-utils"); +var import_provider_utils12 = require("@ai-sdk/provider-utils") +var import_ui_utils9 = require("@ai-sdk/ui-utils") // core/data-stream/create-data-stream.ts -var import_ui_utils = require("@ai-sdk/ui-utils"); +var import_ui_utils = require("@ai-sdk/ui-utils") function createDataStream({ execute, onError = () => "An error occurred." // mask error messages for safety by default }) { - let controller; - const ongoingStreamPromises = []; + let controller + const ongoingStreamPromises = [] const stream = new ReadableStream({ start(controllerArg) { - controller = controllerArg; + controller = controllerArg } - }); + }) function safeEnqueue(data) { try { - controller.enqueue(data); + controller.enqueue(data) } catch (error) { + console.error(error) } } try { const result = execute({ writeData(data) { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("data", [data])); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("data", [data])) }, writeMessageAnnotation(annotation) { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("message_annotations", [annotation])); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("message_annotations", [annotation])) }, merge(streamArg) { ongoingStreamPromises.push( (async () => { - const reader = streamArg.getReader(); + const reader = streamArg.getReader() while (true) { - const { done, value } = await reader.read(); + const { done, value } = await reader.read() if (done) - break; - safeEnqueue(value); + break + safeEnqueue(value) } })().catch((error) => { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))) }) - ); + ) }, onError - }); + }) if (result) { ongoingStreamPromises.push( result.catch((error) => { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))) }) - ); + ) } } catch (error) { - safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))); + safeEnqueue((0, import_ui_utils.formatDataStreamPart)("error", onError(error))) } const waitForStreams = new Promise(async (resolve) => { while (ongoingStreamPromises.length > 0) { - await ongoingStreamPromises.shift(); + await ongoingStreamPromises.shift() } - resolve(); - }); + resolve() + }) waitForStreams.finally(() => { try { - controller.close(); + controller.close() } catch (error) { + console.error(error) } - }); - return stream; + }) + return stream } // core/util/prepare-response-headers.ts @@ -159,14 +163,14 @@ function prepareResponseHeaders(headers, { contentType, dataStreamVersion }) { - const responseHeaders = new Headers(headers != null ? headers : {}); + const responseHeaders = new Headers(headers != null ? headers : {}) if (!responseHeaders.has("Content-Type")) { - responseHeaders.set("Content-Type", contentType); + responseHeaders.set("Content-Type", contentType) } if (dataStreamVersion !== void 0) { - responseHeaders.set("X-Vercel-AI-Data-Stream", dataStreamVersion); + responseHeaders.set("X-Vercel-AI-Data-Stream", dataStreamVersion) } - return responseHeaders; + return responseHeaders } // core/data-stream/create-data-stream-response.ts @@ -187,7 +191,7 @@ function createDataStreamResponse({ dataStreamVersion: "v1" }) } - ); + ) } // core/util/prepare-outgoing-http-headers.ts @@ -195,19 +199,19 @@ function prepareOutgoingHttpHeaders(headers, { contentType, dataStreamVersion }) { - const outgoingHeaders = {}; + const outgoingHeaders = {} if (headers != null) { for (const [key, value] of Object.entries(headers)) { - outgoingHeaders[key] = value; + outgoingHeaders[key] = value } } if (outgoingHeaders["Content-Type"] == null) { - outgoingHeaders["Content-Type"] = contentType; + outgoingHeaders["Content-Type"] = contentType } if (dataStreamVersion !== void 0) { - outgoingHeaders["X-Vercel-AI-Data-Stream"] = dataStreamVersion; + outgoingHeaders["X-Vercel-AI-Data-Stream"] = dataStreamVersion } - return outgoingHeaders; + return outgoingHeaders } // core/util/write-to-server-response.ts @@ -218,23 +222,23 @@ function writeToServerResponse({ headers, stream }) { - response.writeHead(status != null ? status : 200, statusText, headers); - const reader = stream.getReader(); + response.writeHead(status != null ? status : 200, statusText, headers) + const reader = stream.getReader() const read = async () => { try { while (true) { - const { done, value } = await reader.read(); + const { done, value } = await reader.read() if (done) - break; - response.write(value); + break + response.write(value) } } catch (error) { - throw error; + console.error(error) } finally { - response.end(); + response.end() } - }; - read(); + } + read() } // core/data-stream/pipe-data-stream-to-response.ts @@ -256,15 +260,15 @@ function pipeDataStreamToResponse(response, { stream: createDataStream({ execute, onError }).pipeThrough( new TextEncoderStream() ) - }); + }) } // errors/invalid-argument-error.ts -var import_provider = require("@ai-sdk/provider"); -var name = "AI_InvalidArgumentError"; -var marker = `vercel.ai.error.${name}`; -var symbol = Symbol.for(marker); -var _a; +var import_provider = require("@ai-sdk/provider") +var name = "AI_InvalidArgumentError" +var marker = `vercel.ai.error.${name}` +var symbol = Symbol.for(marker) +var _a var InvalidArgumentError = class extends import_provider.AISDKError { constructor({ parameter, @@ -274,49 +278,49 @@ var InvalidArgumentError = class extends import_provider.AISDKError { super({ name, message: `Invalid argument for parameter ${parameter}: ${message}` - }); - this[_a] = true; - this.parameter = parameter; - this.value = value; + }) + this[_a] = true + this.parameter = parameter + this.value = value } static isInstance(error) { - return import_provider.AISDKError.hasMarker(error, marker); + return import_provider.AISDKError.hasMarker(error, marker) } -}; -_a = symbol; +} +_a = symbol // util/retry-with-exponential-backoff.ts -var import_provider3 = require("@ai-sdk/provider"); -var import_provider_utils = require("@ai-sdk/provider-utils"); +var import_provider3 = require("@ai-sdk/provider") +var import_provider_utils = require("@ai-sdk/provider-utils") // util/delay.ts async function delay(delayInMs) { - return delayInMs == null ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayInMs)); + return delayInMs == null ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, delayInMs)) } // util/retry-error.ts -var import_provider2 = require("@ai-sdk/provider"); -var name2 = "AI_RetryError"; -var marker2 = `vercel.ai.error.${name2}`; -var symbol2 = Symbol.for(marker2); -var _a2; +var import_provider2 = require("@ai-sdk/provider") +var name2 = "AI_RetryError" +var marker2 = `vercel.ai.error.${name2}` +var symbol2 = Symbol.for(marker2) +var _a2 var RetryError = class extends import_provider2.AISDKError { constructor({ message, reason, errors }) { - super({ name: name2, message }); - this[_a2] = true; - this.reason = reason; - this.errors = errors; - this.lastError = errors[errors.length - 1]; + super({ name: name2, message }) + this[_a2] = true + this.reason = reason + this.errors = errors + this.lastError = errors[errors.length - 1] } static isInstance(error) { - return import_provider2.AISDKError.hasMarker(error, marker2); + return import_provider2.AISDKError.hasMarker(error, marker2) } -}; -_a2 = symbol2; +} +_a2 = symbol2 // util/retry-with-exponential-backoff.ts var retryWithExponentialBackoff = ({ @@ -327,47 +331,47 @@ var retryWithExponentialBackoff = ({ maxRetries, delayInMs: initialDelayInMs, backoffFactor -}); +}) async function _retryWithExponentialBackoff(f, { maxRetries, delayInMs, backoffFactor }, errors = []) { try { - return await f(); + return await f() } catch (error) { if ((0, import_provider_utils.isAbortError)(error)) { - throw error; + throw error } if (maxRetries === 0) { - throw error; + throw error } - const errorMessage = (0, import_provider_utils.getErrorMessage)(error); - const newErrors = [...errors, error]; - const tryNumber = newErrors.length; + const errorMessage = (0, import_provider_utils.getErrorMessage)(error) + const newErrors = [...errors, error] + const tryNumber = newErrors.length if (tryNumber > maxRetries) { throw new RetryError({ message: `Failed after ${tryNumber} attempts. Last error: ${errorMessage}`, reason: "maxRetriesExceeded", errors: newErrors - }); + }) } if (error instanceof Error && import_provider3.APICallError.isInstance(error) && error.isRetryable === true && tryNumber <= maxRetries) { - await delay(delayInMs); + await delay(delayInMs) return _retryWithExponentialBackoff( f, { maxRetries, delayInMs: backoffFactor * delayInMs, backoffFactor }, newErrors - ); + ) } if (tryNumber === 1) { - throw error; + throw error } throw new RetryError({ message: `Failed after ${tryNumber} attempts with non-retryable error: '${errorMessage}'`, reason: "errorNotRetryable", errors: newErrors - }); + }) } } @@ -381,21 +385,21 @@ function prepareRetries({ parameter: "maxRetries", value: maxRetries, message: "maxRetries must be an integer" - }); + }) } if (maxRetries < 0) { throw new InvalidArgumentError({ parameter: "maxRetries", value: maxRetries, message: "maxRetries must be >= 0" - }); + }) } } - const maxRetriesResult = maxRetries != null ? maxRetries : 2; + const maxRetriesResult = maxRetries != null ? maxRetries : 2 return { maxRetries: maxRetriesResult, retry: retryWithExponentialBackoff({ maxRetries: maxRetriesResult }) - }; + } } // core/telemetry/assemble-operation-name.ts @@ -410,7 +414,7 @@ function assembleOperationName({ // detailed, AI SDK specific data: "ai.operationId": operationId, "ai.telemetry.functionId": telemetry == null ? void 0 : telemetry.functionId - }; + } } // core/telemetry/get-base-telemetry-attributes.ts @@ -420,93 +424,93 @@ function getBaseTelemetryAttributes({ telemetry, headers }) { - var _a14; + var _a14 return { "ai.model.provider": model.provider, "ai.model.id": model.modelId, // settings: ...Object.entries(settings).reduce((attributes, [key, value]) => { - attributes[`ai.settings.${key}`] = value; - return attributes; + attributes[`ai.settings.${key}`] = value + return attributes }, {}), // add metadata as attributes: ...Object.entries((_a14 = telemetry == null ? void 0 : telemetry.metadata) != null ? _a14 : {}).reduce( (attributes, [key, value]) => { - attributes[`ai.telemetry.metadata.${key}`] = value; - return attributes; + attributes[`ai.telemetry.metadata.${key}`] = value + return attributes }, {} ), // request headers ...Object.entries(headers != null ? headers : {}).reduce((attributes, [key, value]) => { if (value !== void 0) { - attributes[`ai.request.headers.${key}`] = value; + attributes[`ai.request.headers.${key}`] = value } - return attributes; + return attributes }, {}) - }; + } } // core/telemetry/get-tracer.ts -var import_api = require("@opentelemetry/api"); +var import_api = require("@opentelemetry/api") // core/telemetry/noop-tracer.ts var noopTracer = { startSpan() { - return noopSpan; + return noopSpan }, startActiveSpan(name14, arg1, arg2, arg3) { if (typeof arg1 === "function") { - return arg1(noopSpan); + return arg1(noopSpan) } if (typeof arg2 === "function") { - return arg2(noopSpan); + return arg2(noopSpan) } if (typeof arg3 === "function") { - return arg3(noopSpan); + return arg3(noopSpan) } } -}; +} var noopSpan = { spanContext() { - return noopSpanContext; + return noopSpanContext }, setAttribute() { - return this; + return this }, setAttributes() { - return this; + return this }, addEvent() { - return this; + return this }, addLink() { - return this; + return this }, addLinks() { - return this; + return this }, setStatus() { - return this; + return this }, updateName() { - return this; + return this }, end() { - return this; + return this }, isRecording() { - return false; + return false }, recordException() { - return this; + return this } -}; +} var noopSpanContext = { traceId: "", spanId: "", traceFlags: 0 -}; +} // core/telemetry/get-tracer.ts function getTracer({ @@ -514,16 +518,16 @@ function getTracer({ tracer } = {}) { if (!isEnabled) { - return noopTracer; + return noopTracer } if (tracer) { - return tracer; + return tracer } - return import_api.trace.getTracer("ai"); + return import_api.trace.getTracer("ai") } // core/telemetry/record-span.ts -var import_api2 = require("@opentelemetry/api"); +var import_api2 = require("@opentelemetry/api") function recordSpan({ name: name14, tracer, @@ -533,11 +537,11 @@ function recordSpan({ }) { return tracer.startActiveSpan(name14, { attributes }, async (span) => { try { - const result = await fn(span); + const result = await fn(span) if (endWhenDone) { - span.end(); + span.end() } - return result; + return result } catch (error) { try { if (error instanceof Error) { @@ -545,20 +549,20 @@ function recordSpan({ name: error.name, message: error.message, stack: error.stack - }); + }) span.setStatus({ code: import_api2.SpanStatusCode.ERROR, message: error.message - }); + }) } else { - span.setStatus({ code: import_api2.SpanStatusCode.ERROR }); + span.setStatus({ code: import_api2.SpanStatusCode.ERROR }) } } finally { - span.end(); + span.end() } - throw error; + throw error } - }); + }) } // core/telemetry/select-telemetry-attributes.ts @@ -567,28 +571,28 @@ function selectTelemetryAttributes({ attributes }) { if ((telemetry == null ? void 0 : telemetry.isEnabled) !== true) { - return {}; + return {} } return Object.entries(attributes).reduce((attributes2, [key, value]) => { if (value === void 0) { - return attributes2; + return attributes2 } if (typeof value === "object" && "input" in value && typeof value.input === "function") { if ((telemetry == null ? void 0 : telemetry.recordInputs) === false) { - return attributes2; + return attributes2 } - const result = value.input(); - return result === void 0 ? attributes2 : { ...attributes2, [key]: result }; + const result = value.input() + return result === void 0 ? attributes2 : { ...attributes2, [key]: result } } if (typeof value === "object" && "output" in value && typeof value.output === "function") { if ((telemetry == null ? void 0 : telemetry.recordOutputs) === false) { - return attributes2; + return attributes2 } - const result = value.output(); - return result === void 0 ? attributes2 : { ...attributes2, [key]: result }; + const result = value.output() + return result === void 0 ? attributes2 : { ...attributes2, [key]: result } } - return { ...attributes2, [key]: value }; - }, {}); + return { ...attributes2, [key]: value } + }, {}) } // core/embed/embed.ts @@ -600,14 +604,14 @@ async function embed({ headers, experimental_telemetry: telemetry }) { - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { maxRetries } - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.embed", attributes: selectTelemetryAttributes({ @@ -639,14 +643,14 @@ async function embed({ }), tracer, fn: async (doEmbedSpan) => { - var _a14; + var _a14 const modelResponse = await model.doEmbed({ values: [value], abortSignal, headers - }); - const embedding2 = modelResponse.embeddings[0]; - const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN }; + }) + const embedding2 = modelResponse.embeddings[0] + const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN } doEmbedSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -659,16 +663,16 @@ async function embed({ "ai.usage.tokens": usage2.tokens } }) - ); + ) return { embedding: embedding2, usage: usage2, rawResponse: modelResponse.rawResponse - }; + } } }) ) - ); + ) span.setAttributes( selectTelemetryAttributes({ telemetry, @@ -677,30 +681,30 @@ async function embed({ "ai.usage.tokens": usage.tokens } }) - ); - return new DefaultEmbedResult({ value, embedding, usage, rawResponse }); + ) + return new DefaultEmbedResult({ value, embedding, usage, rawResponse }) } - }); + }) } var DefaultEmbedResult = class { constructor(options) { - this.value = options.value; - this.embedding = options.embedding; - this.usage = options.usage; - this.rawResponse = options.rawResponse; + this.value = options.value + this.embedding = options.embedding + this.usage = options.usage + this.rawResponse = options.rawResponse } -}; +} // core/util/split-array.ts function splitArray(array, chunkSize) { if (chunkSize <= 0) { - throw new Error("chunkSize must be greater than 0"); + throw new Error("chunkSize must be greater than 0") } - const result = []; + const result = [] for (let i = 0; i < array.length; i += chunkSize) { - result.push(array.slice(i, i + chunkSize)); + result.push(array.slice(i, i + chunkSize)) } - return result; + return result } // core/embed/embed-many.ts @@ -712,14 +716,14 @@ async function embedMany({ headers, experimental_telemetry: telemetry }) { - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { maxRetries } - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.embedMany", attributes: selectTelemetryAttributes({ @@ -735,7 +739,7 @@ async function embedMany({ }), tracer, fn: async (span) => { - const maxEmbeddingsPerCall = model.maxEmbeddingsPerCall; + const maxEmbeddingsPerCall = model.maxEmbeddingsPerCall if (maxEmbeddingsPerCall == null) { const { embeddings: embeddings2, usage } = await retry(() => { return recordSpan({ @@ -756,14 +760,14 @@ async function embedMany({ }), tracer, fn: async (doEmbedSpan) => { - var _a14; + var _a14 const modelResponse = await model.doEmbed({ values, abortSignal, headers - }); - const embeddings3 = modelResponse.embeddings; - const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN }; + }) + const embeddings3 = modelResponse.embeddings + const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN } doEmbedSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -774,11 +778,11 @@ async function embedMany({ "ai.usage.tokens": usage2.tokens } }) - ); - return { embeddings: embeddings3, usage: usage2 }; + ) + return { embeddings: embeddings3, usage: usage2 } } - }); - }); + }) + }) span.setAttributes( selectTelemetryAttributes({ telemetry, @@ -789,12 +793,12 @@ async function embedMany({ "ai.usage.tokens": usage.tokens } }) - ); - return new DefaultEmbedManyResult({ values, embeddings: embeddings2, usage }); + ) + return new DefaultEmbedManyResult({ values, embeddings: embeddings2, usage }) } - const valueChunks = splitArray(values, maxEmbeddingsPerCall); - const embeddings = []; - let tokens = 0; + const valueChunks = splitArray(values, maxEmbeddingsPerCall) + const embeddings = [] + let tokens = 0 for (const chunk of valueChunks) { const { embeddings: responseEmbeddings, usage } = await retry(() => { return recordSpan({ @@ -815,14 +819,14 @@ async function embedMany({ }), tracer, fn: async (doEmbedSpan) => { - var _a14; + var _a14 const modelResponse = await model.doEmbed({ values: chunk, abortSignal, headers - }); - const embeddings2 = modelResponse.embeddings; - const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN }; + }) + const embeddings2 = modelResponse.embeddings + const usage2 = (_a14 = modelResponse.usage) != null ? _a14 : { tokens: NaN } doEmbedSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -833,13 +837,13 @@ async function embedMany({ "ai.usage.tokens": usage2.tokens } }) - ); - return { embeddings: embeddings2, usage: usage2 }; + ) + return { embeddings: embeddings2, usage: usage2 } } - }); - }); - embeddings.push(...responseEmbeddings); - tokens += usage.tokens; + }) + }) + embeddings.push(...responseEmbeddings) + tokens += usage.tokens } span.setAttributes( selectTelemetryAttributes({ @@ -851,25 +855,25 @@ async function embedMany({ "ai.usage.tokens": tokens } }) - ); + ) return new DefaultEmbedManyResult({ values, embeddings, usage: { tokens } - }); + }) } - }); + }) } var DefaultEmbedManyResult = class { constructor(options) { - this.values = options.values; - this.embeddings = options.embeddings; - this.usage = options.usage; + this.values = options.values + this.embeddings = options.embeddings + this.usage = options.usage } -}; +} // core/generate-image/generate-image.ts -var import_provider_utils2 = require("@ai-sdk/provider-utils"); +var import_provider_utils2 = require("@ai-sdk/provider-utils") async function generateImage({ model, prompt, @@ -882,17 +886,17 @@ async function generateImage({ abortSignal, headers }) { - var _a14; - const { retry } = prepareRetries({ maxRetries: maxRetriesArg }); - const maxImagesPerCall = (_a14 = model.maxImagesPerCall) != null ? _a14 : 1; - const callCount = Math.ceil(n / maxImagesPerCall); + var _a14 + const { retry } = prepareRetries({ maxRetries: maxRetriesArg }) + const maxImagesPerCall = (_a14 = model.maxImagesPerCall) != null ? _a14 : 1 + const callCount = Math.ceil(n / maxImagesPerCall) const callImageCounts = Array.from({ length: callCount }, (_, i) => { if (i < callCount - 1) { - return maxImagesPerCall; + return maxImagesPerCall } - const remainder = n % maxImagesPerCall; - return remainder === 0 ? maxImagesPerCall : remainder; - }); + const remainder = n % maxImagesPerCall + return remainder === 0 ? maxImagesPerCall : remainder + }) const results = await Promise.all( callImageCounts.map( async (callImageCount) => retry( @@ -908,57 +912,57 @@ async function generateImage({ }) ) ) - ); - const images = []; - const warnings = []; + ) + const images = [] + const warnings = [] for (const result of results) { images.push( ...result.images.map((image) => new DefaultGeneratedImage({ image })) - ); - warnings.push(...result.warnings); + ) + warnings.push(...result.warnings) } - return new DefaultGenerateImageResult({ images, warnings }); + return new DefaultGenerateImageResult({ images, warnings }) } var DefaultGenerateImageResult = class { constructor(options) { - this.images = options.images; - this.warnings = options.warnings; + this.images = options.images + this.warnings = options.warnings } get image() { - return this.images[0]; + return this.images[0] } -}; +} var DefaultGeneratedImage = class { constructor({ image }) { - const isUint8Array = image instanceof Uint8Array; - this.base64Data = isUint8Array ? void 0 : image; - this.uint8ArrayData = isUint8Array ? image : void 0; + const isUint8Array = image instanceof Uint8Array + this.base64Data = isUint8Array ? void 0 : image + this.uint8ArrayData = isUint8Array ? image : void 0 } // lazy conversion with caching to avoid unnecessary conversion overhead: get base64() { if (this.base64Data == null) { - this.base64Data = (0, import_provider_utils2.convertUint8ArrayToBase64)(this.uint8ArrayData); + this.base64Data = (0, import_provider_utils2.convertUint8ArrayToBase64)(this.uint8ArrayData) } - return this.base64Data; + return this.base64Data } // lazy conversion with caching to avoid unnecessary conversion overhead: get uint8Array() { if (this.uint8ArrayData == null) { - this.uint8ArrayData = (0, import_provider_utils2.convertBase64ToUint8Array)(this.base64Data); + this.uint8ArrayData = (0, import_provider_utils2.convertBase64ToUint8Array)(this.base64Data) } - return this.uint8ArrayData; + return this.uint8ArrayData } -}; +} // core/generate-object/generate-object.ts -var import_provider_utils6 = require("@ai-sdk/provider-utils"); +var import_provider_utils6 = require("@ai-sdk/provider-utils") // errors/no-object-generated-error.ts -var import_provider4 = require("@ai-sdk/provider"); -var name3 = "AI_NoObjectGeneratedError"; -var marker3 = `vercel.ai.error.${name3}`; -var symbol3 = Symbol.for(marker3); -var _a3; +var import_provider4 = require("@ai-sdk/provider") +var name3 = "AI_NoObjectGeneratedError" +var marker3 = `vercel.ai.error.${name3}` +var symbol3 = Symbol.for(marker3) +var _a3 var NoObjectGeneratedError = class extends import_provider4.AISDKError { constructor({ message = "No object generated.", @@ -967,24 +971,24 @@ var NoObjectGeneratedError = class extends import_provider4.AISDKError { response, usage }) { - super({ name: name3, message, cause }); - this[_a3] = true; - this.text = text2; - this.response = response; - this.usage = usage; + super({ name: name3, message, cause }) + this[_a3] = true + this.text = text2 + this.response = response + this.usage = usage } static isInstance(error) { - return import_provider4.AISDKError.hasMarker(error, marker3); + return import_provider4.AISDKError.hasMarker(error, marker3) } -}; -_a3 = symbol3; +} +_a3 = symbol3 // util/download-error.ts -var import_provider5 = require("@ai-sdk/provider"); -var name4 = "AI_DownloadError"; -var marker4 = `vercel.ai.error.${name4}`; -var symbol4 = Symbol.for(marker4); -var _a4; +var import_provider5 = require("@ai-sdk/provider") +var name4 = "AI_DownloadError" +var marker4 = `vercel.ai.error.${name4}` +var symbol4 = Symbol.for(marker4) +var _a4 var DownloadError = class extends import_provider5.AISDKError { constructor({ url, @@ -993,43 +997,43 @@ var DownloadError = class extends import_provider5.AISDKError { cause, message = cause == null ? `Failed to download ${url}: ${statusCode} ${statusText}` : `Failed to download ${url}: ${cause}` }) { - super({ name: name4, message, cause }); - this[_a4] = true; - this.url = url; - this.statusCode = statusCode; - this.statusText = statusText; + super({ name: name4, message, cause }) + this[_a4] = true + this.url = url + this.statusCode = statusCode + this.statusText = statusText } static isInstance(error) { - return import_provider5.AISDKError.hasMarker(error, marker4); + return import_provider5.AISDKError.hasMarker(error, marker4) } -}; -_a4 = symbol4; +} +_a4 = symbol4 // util/download.ts async function download({ url, fetchImplementation = fetch }) { - var _a14; - const urlText = url.toString(); + var _a14 + const urlText = url.toString() try { - const response = await fetchImplementation(urlText); + const response = await fetchImplementation(urlText) if (!response.ok) { throw new DownloadError({ url: urlText, statusCode: response.status, statusText: response.statusText - }); + }) } return { data: new Uint8Array(await response.arrayBuffer()), mimeType: (_a14 = response.headers.get("content-type")) != null ? _a14 : void 0 - }; + } } catch (error) { if (DownloadError.isInstance(error)) { - throw error; + throw error } - throw new DownloadError({ url: urlText, cause: error }); + throw new DownloadError({ url: urlText, cause: error }) } } @@ -1039,43 +1043,43 @@ var mimeTypeSignatures = [ { mimeType: "image/png", bytes: [137, 80, 78, 71] }, { mimeType: "image/jpeg", bytes: [255, 216] }, { mimeType: "image/webp", bytes: [82, 73, 70, 70] } -]; +] function detectImageMimeType(image) { for (const { bytes, mimeType } of mimeTypeSignatures) { if (image.length >= bytes.length && bytes.every((byte, index) => image[index] === byte)) { - return mimeType; + return mimeType } } - return void 0; + return void 0 } // core/prompt/data-content.ts -var import_provider_utils3 = require("@ai-sdk/provider-utils"); +var import_provider_utils3 = require("@ai-sdk/provider-utils") // core/prompt/invalid-data-content-error.ts -var import_provider6 = require("@ai-sdk/provider"); -var name5 = "AI_InvalidDataContentError"; -var marker5 = `vercel.ai.error.${name5}`; -var symbol5 = Symbol.for(marker5); -var _a5; +var import_provider6 = require("@ai-sdk/provider") +var name5 = "AI_InvalidDataContentError" +var marker5 = `vercel.ai.error.${name5}` +var symbol5 = Symbol.for(marker5) +var _a5 var InvalidDataContentError = class extends import_provider6.AISDKError { constructor({ content, cause, message = `Invalid data content. Expected a base64 string, Uint8Array, ArrayBuffer, or Buffer, but got ${typeof content}.` }) { - super({ name: name5, message, cause }); - this[_a5] = true; - this.content = content; + super({ name: name5, message, cause }) + this[_a5] = true + this.content = content } static isInstance(error) { - return import_provider6.AISDKError.hasMarker(error, marker5); + return import_provider6.AISDKError.hasMarker(error, marker5) } -}; -_a5 = symbol5; +} +_a5 = symbol5 // core/prompt/data-content.ts -var import_zod = require("zod"); +var import_zod = require("zod") var dataContentSchema = import_zod.z.union([ import_zod.z.string(), import_zod.z.instanceof(Uint8Array), @@ -1083,83 +1087,83 @@ var dataContentSchema = import_zod.z.union([ import_zod.z.custom( // Buffer might not be available in some environments such as CloudFlare: (value) => { - var _a14, _b; - return (_b = (_a14 = globalThis.Buffer) == null ? void 0 : _a14.isBuffer(value)) != null ? _b : false; + var _a14, _b + return (_b = (_a14 = globalThis.Buffer) == null ? void 0 : _a14.isBuffer(value)) != null ? _b : false }, { message: "Must be a Buffer" } ) -]); +]) function convertDataContentToBase64String(content) { if (typeof content === "string") { - return content; + return content } if (content instanceof ArrayBuffer) { - return (0, import_provider_utils3.convertUint8ArrayToBase64)(new Uint8Array(content)); + return (0, import_provider_utils3.convertUint8ArrayToBase64)(new Uint8Array(content)) } - return (0, import_provider_utils3.convertUint8ArrayToBase64)(content); + return (0, import_provider_utils3.convertUint8ArrayToBase64)(content) } function convertDataContentToUint8Array(content) { if (content instanceof Uint8Array) { - return content; + return content } if (typeof content === "string") { try { - return (0, import_provider_utils3.convertBase64ToUint8Array)(content); + return (0, import_provider_utils3.convertBase64ToUint8Array)(content) } catch (error) { throw new InvalidDataContentError({ message: "Invalid data content. Content string is not a base64-encoded media.", content, cause: error - }); + }) } } if (content instanceof ArrayBuffer) { - return new Uint8Array(content); + return new Uint8Array(content) } - throw new InvalidDataContentError({ content }); + throw new InvalidDataContentError({ content }) } function convertUint8ArrayToText(uint8Array) { try { - return new TextDecoder().decode(uint8Array); + return new TextDecoder().decode(uint8Array) } catch (error) { - throw new Error("Error decoding Uint8Array to text"); + throw new Error("Error decoding Uint8Array to text") } } // core/prompt/invalid-message-role-error.ts -var import_provider7 = require("@ai-sdk/provider"); -var name6 = "AI_InvalidMessageRoleError"; -var marker6 = `vercel.ai.error.${name6}`; -var symbol6 = Symbol.for(marker6); -var _a6; +var import_provider7 = require("@ai-sdk/provider") +var name6 = "AI_InvalidMessageRoleError" +var marker6 = `vercel.ai.error.${name6}` +var symbol6 = Symbol.for(marker6) +var _a6 var InvalidMessageRoleError = class extends import_provider7.AISDKError { constructor({ role, message = `Invalid message role: '${role}'. Must be one of: "system", "user", "assistant", "tool".` }) { - super({ name: name6, message }); - this[_a6] = true; - this.role = role; + super({ name: name6, message }) + this[_a6] = true + this.role = role } static isInstance(error) { - return import_provider7.AISDKError.hasMarker(error, marker6); + return import_provider7.AISDKError.hasMarker(error, marker6) } -}; -_a6 = symbol6; +} +_a6 = symbol6 // core/prompt/split-data-url.ts function splitDataUrl(dataUrl) { try { - const [header, base64Content] = dataUrl.split(","); + const [header, base64Content] = dataUrl.split(",") return { mimeType: header.split(";")[0].split(":")[1], base64Content - }; + } } catch (error) { return { mimeType: void 0, base64Content: void 0 - }; + } } } @@ -1175,23 +1179,23 @@ async function convertToLanguageModelPrompt({ downloadImplementation, modelSupportsImageUrls, modelSupportsUrl - ); + ) return [ ...prompt.system != null ? [{ role: "system", content: prompt.system }] : [], ...prompt.messages.map( (message) => convertToLanguageModelMessage(message, downloadedAssets) ) - ]; + ] } function convertToLanguageModelMessage(message, downloadedAssets) { - const role = message.role; + const role = message.role switch (role) { case "system": { return { role: "system", content: message.content, providerMetadata: message.experimental_providerMetadata - }; + } } case "user": { if (typeof message.content === "string") { @@ -1199,13 +1203,13 @@ function convertToLanguageModelMessage(message, downloadedAssets) { role: "user", content: [{ type: "text", text: message.content }], providerMetadata: message.experimental_providerMetadata - }; + } } return { role: "user", content: message.content.map((part) => convertPartToLanguageModelPart(part, downloadedAssets)).filter((part) => part.type !== "text" || part.text !== ""), providerMetadata: message.experimental_providerMetadata - }; + } } case "assistant": { if (typeof message.content === "string") { @@ -1213,7 +1217,7 @@ function convertToLanguageModelMessage(message, downloadedAssets) { role: "assistant", content: [{ type: "text", text: message.content }], providerMetadata: message.experimental_providerMetadata - }; + } } return { role: "assistant", @@ -1221,14 +1225,14 @@ function convertToLanguageModelMessage(message, downloadedAssets) { // remove empty text parts: (part) => part.type !== "text" || part.text !== "" ).map((part) => { - const { experimental_providerMetadata, ...rest } = part; + const { experimental_providerMetadata, ...rest } = part return { ...rest, providerMetadata: experimental_providerMetadata - }; + } }), providerMetadata: message.experimental_providerMetadata - }; + } } case "tool": { return { @@ -1243,11 +1247,11 @@ function convertToLanguageModelMessage(message, downloadedAssets) { providerMetadata: part.experimental_providerMetadata })), providerMetadata: message.experimental_providerMetadata - }; + } } default: { - const _exhaustiveCheck = role; - throw new InvalidMessageRoleError({ role: _exhaustiveCheck }); + const _exhaustiveCheck = role + throw new InvalidMessageRoleError({ role: _exhaustiveCheck }) } } } @@ -1263,90 +1267,90 @@ async function downloadAssets(messages, downloadImplementation, modelSupportsIma // support string urls: typeof part === "string" && (part.startsWith("http:") || part.startsWith("https:")) ? new URL(part) : part ) - ).filter((image) => image instanceof URL).filter((url) => !modelSupportsUrl(url)); + ).filter((image) => image instanceof URL).filter((url) => !modelSupportsUrl(url)) const downloadedImages = await Promise.all( urls.map(async (url) => ({ url, data: await downloadImplementation({ url }) })) - ); + ) return Object.fromEntries( downloadedImages.map(({ url, data }) => [url.toString(), data]) - ); + ) } function convertPartToLanguageModelPart(part, downloadedAssets) { - var _a14; + var _a14 if (part.type === "text") { return { type: "text", text: part.text, providerMetadata: part.experimental_providerMetadata - }; + } } - let mimeType = part.mimeType; - let data; - let content; - let normalizedData; - const type = part.type; + let mimeType = part.mimeType + let data + let content + let normalizedData + const type = part.type switch (type) { case "image": - data = part.image; - break; + data = part.image + break case "file": - data = part.data; - break; + data = part.data + break default: - throw new Error(`Unsupported part type: ${type}`); + throw new Error(`Unsupported part type: ${type}`) } try { - content = typeof data === "string" ? new URL(data) : data; + content = typeof data === "string" ? new URL(data) : data } catch (error) { - content = data; + content = data } if (content instanceof URL) { if (content.protocol === "data:") { const { mimeType: dataUrlMimeType, base64Content } = splitDataUrl( content.toString() - ); + ) if (dataUrlMimeType == null || base64Content == null) { - throw new Error(`Invalid data URL format in part ${type}`); + throw new Error(`Invalid data URL format in part ${type}`) } - mimeType = dataUrlMimeType; - normalizedData = convertDataContentToUint8Array(base64Content); + mimeType = dataUrlMimeType + normalizedData = convertDataContentToUint8Array(base64Content) } else { - const downloadedFile = downloadedAssets[content.toString()]; + const downloadedFile = downloadedAssets[content.toString()] if (downloadedFile) { - normalizedData = downloadedFile.data; - mimeType != null ? mimeType : mimeType = downloadedFile.mimeType; + normalizedData = downloadedFile.data + mimeType != null ? mimeType : mimeType = downloadedFile.mimeType } else { - normalizedData = content; + normalizedData = content } } } else { - normalizedData = convertDataContentToUint8Array(content); + normalizedData = convertDataContentToUint8Array(content) } switch (type) { case "image": { if (normalizedData instanceof Uint8Array) { - mimeType = (_a14 = detectImageMimeType(normalizedData)) != null ? _a14 : mimeType; + mimeType = (_a14 = detectImageMimeType(normalizedData)) != null ? _a14 : mimeType } return { type: "image", image: normalizedData, mimeType, providerMetadata: part.experimental_providerMetadata - }; + } } case "file": { if (mimeType == null) { - throw new Error(`Mime type is missing for file part`); + throw new Error(`Mime type is missing for file part`) } return { type: "file", data: normalizedData instanceof Uint8Array ? convertDataContentToBase64String(normalizedData) : normalizedData, mimeType, providerMetadata: part.experimental_providerMetadata - }; + } } } } @@ -1368,14 +1372,14 @@ function prepareCallSettings({ parameter: "maxTokens", value: maxTokens, message: "maxTokens must be an integer" - }); + }) } if (maxTokens < 1) { throw new InvalidArgumentError({ parameter: "maxTokens", value: maxTokens, message: "maxTokens must be >= 1" - }); + }) } } if (temperature != null) { @@ -1384,7 +1388,7 @@ function prepareCallSettings({ parameter: "temperature", value: temperature, message: "temperature must be a number" - }); + }) } } if (topP != null) { @@ -1393,7 +1397,7 @@ function prepareCallSettings({ parameter: "topP", value: topP, message: "topP must be a number" - }); + }) } } if (topK != null) { @@ -1402,7 +1406,7 @@ function prepareCallSettings({ parameter: "topK", value: topK, message: "topK must be a number" - }); + }) } } if (presencePenalty != null) { @@ -1411,7 +1415,7 @@ function prepareCallSettings({ parameter: "presencePenalty", value: presencePenalty, message: "presencePenalty must be a number" - }); + }) } } if (frequencyPenalty != null) { @@ -1420,7 +1424,7 @@ function prepareCallSettings({ parameter: "frequencyPenalty", value: frequencyPenalty, message: "frequencyPenalty must be a number" - }); + }) } } if (seed != null) { @@ -1429,7 +1433,7 @@ function prepareCallSettings({ parameter: "seed", value: seed, message: "seed must be an integer" - }); + }) } } return { @@ -1441,22 +1445,22 @@ function prepareCallSettings({ frequencyPenalty, stopSequences: stopSequences != null && stopSequences.length > 0 ? stopSequences : void 0, seed - }; + } } // core/prompt/standardize-prompt.ts -var import_provider9 = require("@ai-sdk/provider"); -var import_provider_utils4 = require("@ai-sdk/provider-utils"); -var import_zod7 = require("zod"); +var import_provider9 = require("@ai-sdk/provider") +var import_provider_utils4 = require("@ai-sdk/provider-utils") +var import_zod7 = require("zod") // core/prompt/message.ts -var import_zod6 = require("zod"); +var import_zod6 = require("zod") // core/types/provider-metadata.ts -var import_zod3 = require("zod"); +var import_zod3 = require("zod") // core/types/json-value.ts -var import_zod2 = require("zod"); +var import_zod2 = require("zod") var jsonValueSchema = import_zod2.z.lazy( () => import_zod2.z.union([ import_zod2.z.null(), @@ -1466,19 +1470,19 @@ var jsonValueSchema = import_zod2.z.lazy( import_zod2.z.record(import_zod2.z.string(), jsonValueSchema), import_zod2.z.array(jsonValueSchema) ]) -); +) // core/types/provider-metadata.ts var providerMetadataSchema = import_zod3.z.record( import_zod3.z.string(), import_zod3.z.record(import_zod3.z.string(), jsonValueSchema) -); +) // core/prompt/content-part.ts -var import_zod5 = require("zod"); +var import_zod5 = require("zod") // core/prompt/tool-result-content.ts -var import_zod4 = require("zod"); +var import_zod4 = require("zod") var toolResultContentSchema = import_zod4.z.array( import_zod4.z.union([ import_zod4.z.object({ type: import_zod4.z.literal("text"), text: import_zod4.z.string() }), @@ -1488,32 +1492,32 @@ var toolResultContentSchema = import_zod4.z.array( mimeType: import_zod4.z.string().optional() }) ]) -); +) // core/prompt/content-part.ts var textPartSchema = import_zod5.z.object({ type: import_zod5.z.literal("text"), text: import_zod5.z.string(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var imagePartSchema = import_zod5.z.object({ type: import_zod5.z.literal("image"), image: import_zod5.z.union([dataContentSchema, import_zod5.z.instanceof(URL)]), mimeType: import_zod5.z.string().optional(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var filePartSchema = import_zod5.z.object({ type: import_zod5.z.literal("file"), data: import_zod5.z.union([dataContentSchema, import_zod5.z.instanceof(URL)]), mimeType: import_zod5.z.string(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var toolCallPartSchema = import_zod5.z.object({ type: import_zod5.z.literal("tool-call"), toolCallId: import_zod5.z.string(), toolName: import_zod5.z.string(), args: import_zod5.z.unknown() -}); +}) var toolResultPartSchema = import_zod5.z.object({ type: import_zod5.z.literal("tool-result"), toolCallId: import_zod5.z.string(), @@ -1522,14 +1526,14 @@ var toolResultPartSchema = import_zod5.z.object({ content: toolResultContentSchema.optional(), isError: import_zod5.z.boolean().optional(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) // core/prompt/message.ts var coreSystemMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("system"), content: import_zod6.z.string(), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreUserMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("user"), content: import_zod6.z.union([ @@ -1537,7 +1541,7 @@ var coreUserMessageSchema = import_zod6.z.object({ import_zod6.z.array(import_zod6.z.union([textPartSchema, imagePartSchema, filePartSchema])) ]), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreAssistantMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("assistant"), content: import_zod6.z.union([ @@ -1545,36 +1549,36 @@ var coreAssistantMessageSchema = import_zod6.z.object({ import_zod6.z.array(import_zod6.z.union([textPartSchema, toolCallPartSchema])) ]), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreToolMessageSchema = import_zod6.z.object({ role: import_zod6.z.literal("tool"), content: import_zod6.z.array(toolResultPartSchema), experimental_providerMetadata: providerMetadataSchema.optional() -}); +}) var coreMessageSchema = import_zod6.z.union([ coreSystemMessageSchema, coreUserMessageSchema, coreAssistantMessageSchema, coreToolMessageSchema -]); +]) // core/prompt/detect-prompt-type.ts function detectPromptType(prompt) { if (!Array.isArray(prompt)) { - return "other"; + return "other" } if (prompt.length === 0) { - return "messages"; + return "messages" } - const characteristics = prompt.map(detectSingleMessageCharacteristics); + const characteristics = prompt.map(detectSingleMessageCharacteristics) if (characteristics.some((c) => c === "has-ui-specific-parts")) { - return "ui-messages"; + return "ui-messages" } else if (characteristics.every( (c) => c === "has-core-specific-parts" || c === "message" )) { - return "messages"; + return "messages" } else { - return "other"; + return "other" } } function detectSingleMessageCharacteristics(message) { @@ -1582,129 +1586,129 @@ function detectSingleMessageCharacteristics(message) { message.role === "data" || // UI-only role "toolInvocations" in message || // UI-specific field "experimental_attachments" in message)) { - return "has-ui-specific-parts"; + return "has-ui-specific-parts" } else if (typeof message === "object" && message !== null && "content" in message && (Array.isArray(message.content) || // Core messages can have array content "experimental_providerMetadata" in message)) { - return "has-core-specific-parts"; + return "has-core-specific-parts" } else if (typeof message === "object" && message !== null && "role" in message && "content" in message && typeof message.content === "string" && ["system", "user", "assistant", "tool"].includes(message.role)) { - return "message"; + return "message" } else { - return "other"; + return "other" } } // core/prompt/attachments-to-parts.ts function attachmentsToParts(attachments) { - var _a14, _b, _c; - const parts = []; + var _a14, _b, _c + const parts = [] for (const attachment of attachments) { - let url; + let url try { - url = new URL(attachment.url); + url = new URL(attachment.url) } catch (error) { - throw new Error(`Invalid URL: ${attachment.url}`); + throw new Error(`Invalid URL: ${attachment.url}`) } switch (url.protocol) { case "http:": case "https:": { if ((_a14 = attachment.contentType) == null ? void 0 : _a14.startsWith("image/")) { - parts.push({ type: "image", image: url }); + parts.push({ type: "image", image: url }) } else { if (!attachment.contentType) { throw new Error( "If the attachment is not an image, it must specify a content type" - ); + ) } parts.push({ type: "file", data: url, mimeType: attachment.contentType - }); + }) } - break; + break } case "data:": { - let header; - let base64Content; - let mimeType; + let header + let base64Content + let mimeType try { - [header, base64Content] = attachment.url.split(","); - mimeType = header.split(";")[0].split(":")[1]; + [header, base64Content] = attachment.url.split(",") + mimeType = header.split(";")[0].split(":")[1] } catch (error) { - throw new Error(`Error processing data URL: ${attachment.url}`); + throw new Error(`Error processing data URL: ${attachment.url}`) } if (mimeType == null || base64Content == null) { - throw new Error(`Invalid data URL format: ${attachment.url}`); + throw new Error(`Invalid data URL format: ${attachment.url}`) } if ((_b = attachment.contentType) == null ? void 0 : _b.startsWith("image/")) { parts.push({ type: "image", image: convertDataContentToUint8Array(base64Content) - }); + }) } else if ((_c = attachment.contentType) == null ? void 0 : _c.startsWith("text/")) { parts.push({ type: "text", text: convertUint8ArrayToText( convertDataContentToUint8Array(base64Content) ) - }); + }) } else { if (!attachment.contentType) { throw new Error( "If the attachment is not an image or text, it must specify a content type" - ); + ) } parts.push({ type: "file", data: base64Content, mimeType: attachment.contentType - }); + }) } - break; + break } default: { - throw new Error(`Unsupported URL protocol: ${url.protocol}`); + throw new Error(`Unsupported URL protocol: ${url.protocol}`) } } } - return parts; + return parts } // core/prompt/message-conversion-error.ts -var import_provider8 = require("@ai-sdk/provider"); -var name7 = "AI_MessageConversionError"; -var marker7 = `vercel.ai.error.${name7}`; -var symbol7 = Symbol.for(marker7); -var _a7; +var import_provider8 = require("@ai-sdk/provider") +var name7 = "AI_MessageConversionError" +var marker7 = `vercel.ai.error.${name7}` +var symbol7 = Symbol.for(marker7) +var _a7 var MessageConversionError = class extends import_provider8.AISDKError { constructor({ originalMessage, message }) { - super({ name: name7, message }); - this[_a7] = true; - this.originalMessage = originalMessage; + super({ name: name7, message }) + this[_a7] = true + this.originalMessage = originalMessage } static isInstance(error) { - return import_provider8.AISDKError.hasMarker(error, marker7); + return import_provider8.AISDKError.hasMarker(error, marker7) } -}; -_a7 = symbol7; +} +_a7 = symbol7 // core/prompt/convert-to-core-messages.ts function convertToCoreMessages(messages, options) { - var _a14; - const tools = (_a14 = options == null ? void 0 : options.tools) != null ? _a14 : {}; - const coreMessages = []; + var _a14 + const tools = (_a14 = options == null ? void 0 : options.tools) != null ? _a14 : {} + const coreMessages = [] for (const message of messages) { - const { role, content, toolInvocations, experimental_attachments } = message; + const { role, content, toolInvocations, experimental_attachments } = message switch (role) { case "system": { coreMessages.push({ role: "system", content - }); - break; + }) + break } case "user": { coreMessages.push({ @@ -1713,13 +1717,13 @@ function convertToCoreMessages(messages, options) { { type: "text", text: content }, ...attachmentsToParts(experimental_attachments) ] : content - }); - break; + }) + break } case "assistant": { if (toolInvocations == null || toolInvocations.length === 0) { - coreMessages.push({ role: "assistant", content }); - break; + coreMessages.push({ role: "assistant", content }) + break } coreMessages.push({ role: "assistant", @@ -1734,7 +1738,7 @@ function convertToCoreMessages(messages, options) { }) ) ] - }); + }) coreMessages.push({ role: "tool", content: toolInvocations.map((toolInvocation) => { @@ -1742,10 +1746,10 @@ function convertToCoreMessages(messages, options) { throw new MessageConversionError({ originalMessage: message, message: "ToolInvocation must have a result: " + JSON.stringify(toolInvocation) - }); + }) } - const { toolCallId, toolName, result } = toolInvocation; - const tool2 = tools[toolName]; + const { toolCallId, toolName, result } = toolInvocation + const tool2 = tools[toolName] return (tool2 == null ? void 0 : tool2.experimental_toToolResultContent) != null ? { type: "tool-result", toolCallId, @@ -1757,24 +1761,24 @@ function convertToCoreMessages(messages, options) { toolCallId, toolName, result - }; + } }) - }); - break; + }) + break } case "data": { - break; + break } default: { - const _exhaustiveCheck = role; + const _exhaustiveCheck = role throw new MessageConversionError({ originalMessage: message, message: `Unsupported role: ${_exhaustiveCheck}` - }); + }) } } } - return coreMessages; + return coreMessages } // core/prompt/standardize-prompt.ts @@ -1786,26 +1790,26 @@ function standardizePrompt({ throw new import_provider9.InvalidPromptError({ prompt, message: "prompt or messages must be defined" - }); + }) } if (prompt.prompt != null && prompt.messages != null) { throw new import_provider9.InvalidPromptError({ prompt, message: "prompt and messages cannot be defined at the same time" - }); + }) } if (prompt.system != null && typeof prompt.system !== "string") { throw new import_provider9.InvalidPromptError({ prompt, message: "system must be a string" - }); + }) } if (prompt.prompt != null) { if (typeof prompt.prompt !== "string") { throw new import_provider9.InvalidPromptError({ prompt, message: "prompt must be a string" - }); + }) } return { type: "prompt", @@ -1816,37 +1820,37 @@ function standardizePrompt({ content: prompt.prompt } ] - }; + } } if (prompt.messages != null) { - const promptType = detectPromptType(prompt.messages); + const promptType = detectPromptType(prompt.messages) if (promptType === "other") { throw new import_provider9.InvalidPromptError({ prompt, message: "messages must be an array of CoreMessage or UIMessage" - }); + }) } const messages = promptType === "ui-messages" ? convertToCoreMessages(prompt.messages, { tools - }) : prompt.messages; + }) : prompt.messages const validationResult = (0, import_provider_utils4.safeValidateTypes)({ value: messages, schema: import_zod7.z.array(coreMessageSchema) - }); + }) if (!validationResult.success) { throw new import_provider9.InvalidPromptError({ prompt, message: "messages must be an array of CoreMessage or UIMessage", cause: validationResult.error - }); + }) } return { type: "messages", messages, system: prompt.system - }; + } } - throw new Error("unreachable"); + throw new Error("unreachable") } // core/types/usage.ts @@ -1858,20 +1862,20 @@ function calculateLanguageModelUsage({ promptTokens, completionTokens, totalTokens: promptTokens + completionTokens - }; + } } function addLanguageModelUsage(usage1, usage2) { return { promptTokens: usage1.promptTokens + usage2.promptTokens, completionTokens: usage1.completionTokens + usage2.completionTokens, totalTokens: usage1.totalTokens + usage2.totalTokens - }; + } } // core/generate-object/inject-json-instruction.ts -var DEFAULT_SCHEMA_PREFIX = "JSON schema:"; -var DEFAULT_SCHEMA_SUFFIX = "You MUST answer with a JSON object that matches the JSON schema above."; -var DEFAULT_GENERIC_SUFFIX = "You MUST answer with JSON."; +var DEFAULT_SCHEMA_PREFIX = "JSON schema:" +var DEFAULT_SCHEMA_SUFFIX = "You MUST answer with a JSON object that matches the JSON schema above." +var DEFAULT_GENERIC_SUFFIX = "You MUST answer with JSON." function injectJsonInstruction({ prompt, schema, @@ -1885,27 +1889,27 @@ function injectJsonInstruction({ schemaPrefix, schema != null ? JSON.stringify(schema) : void 0, schemaSuffix - ].filter((line) => line != null).join("\n"); + ].filter((line) => line != null).join("\n") } // core/generate-object/output-strategy.ts -var import_provider10 = require("@ai-sdk/provider"); -var import_provider_utils5 = require("@ai-sdk/provider-utils"); -var import_ui_utils2 = require("@ai-sdk/ui-utils"); +var import_provider10 = require("@ai-sdk/provider") +var import_provider_utils5 = require("@ai-sdk/provider-utils") +var import_ui_utils2 = require("@ai-sdk/ui-utils") // core/util/async-iterable-stream.ts function createAsyncIterableStream(source) { - const stream = source.pipeThrough(new TransformStream()); + const stream = source.pipeThrough(new TransformStream()) stream[Symbol.asyncIterator] = () => { - const reader = stream.getReader(); + const reader = stream.getReader() return { async next() { - const { done, value } = await reader.read(); - return done ? { done: true, value: void 0 } : { done: false, value }; + const { done, value } = await reader.read() + return done ? { done: true, value: void 0 } : { done: false, value } } - }; - }; - return stream; + } + } + return stream } // core/generate-object/output-strategy.ts @@ -1913,7 +1917,7 @@ var noSchemaOutputStrategy = { type: "no-schema", jsonSchema: void 0, validatePartialResult({ value, textDelta }) { - return { success: true, value: { partial: value, textDelta } }; + return { success: true, value: { partial: value, textDelta } } }, validateFinalResult(value, context) { return value === void 0 ? { @@ -1924,14 +1928,14 @@ var noSchemaOutputStrategy = { response: context.response, usage: context.usage }) - } : { success: true, value }; + } : { success: true, value } }, createElementStream() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "element streams in no-schema mode" - }); + }) } -}; +} var objectOutputStrategy = (schema) => ({ type: "object", jsonSchema: schema.jsonSchema, @@ -1943,19 +1947,19 @@ var objectOutputStrategy = (schema) => ({ partial: value, textDelta } - }; + } }, validateFinalResult(value) { - return (0, import_provider_utils5.safeValidateTypes)({ value, schema }); + return (0, import_provider_utils5.safeValidateTypes)({ value, schema }) }, createElementStream() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "element streams in object mode" - }); + }) } -}); +}) var arrayOutputStrategy = (schema) => { - const { $schema, ...itemSchema } = schema.jsonSchema; + const { $schema, ...itemSchema } = schema.jsonSchema return { type: "enum", // wrap in object that contains array of elements, since most LLMs will not @@ -1971,7 +1975,7 @@ var arrayOutputStrategy = (schema) => { additionalProperties: false }, validatePartialResult({ value, latestObject, isFirstDelta, isFinalDelta }) { - var _a14; + var _a14 if (!(0, import_provider10.isJSONObject)(value) || !(0, import_provider10.isJSONArray)(value.elements)) { return { success: false, @@ -1979,32 +1983,32 @@ var arrayOutputStrategy = (schema) => { value, cause: "value must be an object that contains an array of elements" }) - }; + } } - const inputArray = value.elements; - const resultArray = []; + const inputArray = value.elements + const resultArray = [] for (let i = 0; i < inputArray.length; i++) { - const element = inputArray[i]; - const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }); + const element = inputArray[i] + const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }) if (i === inputArray.length - 1 && !isFinalDelta) { - continue; + continue } if (!result.success) { - return result; + return result } - resultArray.push(result.value); + resultArray.push(result.value) } - const publishedElementCount = (_a14 = latestObject == null ? void 0 : latestObject.length) != null ? _a14 : 0; - let textDelta = ""; + const publishedElementCount = (_a14 = latestObject == null ? void 0 : latestObject.length) != null ? _a14 : 0 + let textDelta = "" if (isFirstDelta) { - textDelta += "["; + textDelta += "[" } if (publishedElementCount > 0) { - textDelta += ","; + textDelta += "," } - textDelta += resultArray.slice(publishedElementCount).map((element) => JSON.stringify(element)).join(","); + textDelta += resultArray.slice(publishedElementCount).map((element) => JSON.stringify(element)).join(",") if (isFinalDelta) { - textDelta += "]"; + textDelta += "]" } return { success: true, @@ -2012,7 +2016,7 @@ var arrayOutputStrategy = (schema) => { partial: resultArray, textDelta } - }; + } }, validateFinalResult(value) { if (!(0, import_provider10.isJSONObject)(value) || !(0, import_provider10.isJSONArray)(value.elements)) { @@ -2022,51 +2026,51 @@ var arrayOutputStrategy = (schema) => { value, cause: "value must be an object that contains an array of elements" }) - }; - } - const inputArray = value.elements; - for (const element of inputArray) { - const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }); - if (!result.success) { - return result; } } - return { success: true, value: inputArray }; + const inputArray = value.elements + for (const element of inputArray) { + const result = (0, import_provider_utils5.safeValidateTypes)({ value: element, schema }) + if (!result.success) { + return result + } + } + return { success: true, value: inputArray } }, createElementStream(originalStream) { - let publishedElements = 0; + let publishedElements = 0 return createAsyncIterableStream( originalStream.pipeThrough( new TransformStream({ transform(chunk, controller) { switch (chunk.type) { case "object": { - const array = chunk.object; + const array = chunk.object for (; publishedElements < array.length; publishedElements++) { - controller.enqueue(array[publishedElements]); + controller.enqueue(array[publishedElements]) } - break; + break } case "text-delta": case "finish": - break; + break case "error": - controller.error(chunk.error); - break; + controller.error(chunk.error) + break default: { - const _exhaustiveCheck = chunk; + const _exhaustiveCheck = chunk throw new Error( `Unsupported chunk type: ${_exhaustiveCheck}` - ); + ) } } } }) ) - ); + ) } - }; -}; + } +} var enumOutputStrategy = (enumValues) => { return { type: "enum", @@ -2090,29 +2094,29 @@ var enumOutputStrategy = (enumValues) => { value, cause: 'value must be an object that contains a string in the "result" property.' }) - }; + } } - const result = value.result; + const result = value.result return enumValues.includes(result) ? { success: true, value: result } : { success: false, error: new import_provider10.TypeValidationError({ value, cause: "value must be a string in the enum" }) - }; + } }, validatePartialResult() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "partial results in enum mode" - }); + }) }, createElementStream() { throw new import_provider10.UnsupportedFunctionalityError({ functionality: "element streams in enum mode" - }); + }) } - }; -}; + } +} function getOutputStrategy({ output, schema, @@ -2120,16 +2124,16 @@ function getOutputStrategy({ }) { switch (output) { case "object": - return objectOutputStrategy((0, import_ui_utils2.asSchema)(schema)); + return objectOutputStrategy((0, import_ui_utils2.asSchema)(schema)) case "array": - return arrayOutputStrategy((0, import_ui_utils2.asSchema)(schema)); + return arrayOutputStrategy((0, import_ui_utils2.asSchema)(schema)) case "enum": - return enumOutputStrategy(enumValues); + return enumOutputStrategy(enumValues) case "no-schema": - return noSchemaOutputStrategy; + return noSchemaOutputStrategy default: { - const _exhaustiveCheck = output; - throw new Error(`Unsupported output: ${_exhaustiveCheck}`); + const _exhaustiveCheck = output + throw new Error(`Unsupported output: ${_exhaustiveCheck}`) } } } @@ -2148,7 +2152,7 @@ function validateObjectGenerationInput({ parameter: "output", value: output, message: "Invalid output type." - }); + }) } if (output === "no-schema") { if (mode === "auto" || mode === "tool") { @@ -2156,35 +2160,35 @@ function validateObjectGenerationInput({ parameter: "mode", value: mode, message: 'Mode must be "json" for no-schema output.' - }); + }) } if (schema != null) { throw new InvalidArgumentError({ parameter: "schema", value: schema, message: "Schema is not supported for no-schema output." - }); + }) } if (schemaDescription != null) { throw new InvalidArgumentError({ parameter: "schemaDescription", value: schemaDescription, message: "Schema description is not supported for no-schema output." - }); + }) } if (schemaName != null) { throw new InvalidArgumentError({ parameter: "schemaName", value: schemaName, message: "Schema name is not supported for no-schema output." - }); + }) } if (enumValues != null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are not supported for no-schema output." - }); + }) } } if (output === "object") { @@ -2193,14 +2197,14 @@ function validateObjectGenerationInput({ parameter: "schema", value: schema, message: "Schema is required for object output." - }); + }) } if (enumValues != null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are not supported for object output." - }); + }) } } if (output === "array") { @@ -2209,14 +2213,14 @@ function validateObjectGenerationInput({ parameter: "schema", value: schema, message: "Element schema is required for array output." - }); + }) } if (enumValues != null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are not supported for array output." - }); + }) } } if (output === "enum") { @@ -2225,28 +2229,28 @@ function validateObjectGenerationInput({ parameter: "schema", value: schema, message: "Schema is not supported for enum output." - }); + }) } if (schemaDescription != null) { throw new InvalidArgumentError({ parameter: "schemaDescription", value: schemaDescription, message: "Schema description is not supported for enum output." - }); + }) } if (schemaName != null) { throw new InvalidArgumentError({ parameter: "schemaName", value: schemaName, message: "Schema name is not supported for enum output." - }); + }) } if (enumValues == null) { throw new InvalidArgumentError({ parameter: "enumValues", value: enumValues, message: "Enum values are required for enum output." - }); + }) } for (const value of enumValues) { if (typeof value !== "string") { @@ -2254,14 +2258,14 @@ function validateObjectGenerationInput({ parameter: "enumValues", value, message: "Enum values must be strings." - }); + }) } } } } // core/generate-object/generate-object.ts -var originalGenerateId = (0, import_provider_utils6.createIdGenerator)({ prefix: "aiobj", size: 24 }); +var originalGenerateId = (0, import_provider_utils6.createIdGenerator)({ prefix: "aiobj", size: 24 }) async function generateObject({ model, enum: enumValues, @@ -2292,23 +2296,23 @@ async function generateObject({ schemaName, schemaDescription, enumValues - }); - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + }) + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const outputStrategy = getOutputStrategy({ output, schema: inputSchema, enumValues - }); + }) if (outputStrategy.type === "no-schema" && mode === void 0) { - mode = "json"; + mode = "json" } const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.generateObject", attributes: selectTelemetryAttributes({ @@ -2332,19 +2336,19 @@ async function generateObject({ }), tracer, fn: async (span) => { - var _a14, _b; + var _a14, _b if (mode === "auto" || mode == null) { - mode = model.defaultObjectGenerationMode; + mode = model.defaultObjectGenerationMode } - let result; - let finishReason; - let usage; - let warnings; - let rawResponse; - let response; - let request; - let logprobs; - let resultProviderMetadata; + let result + let finishReason + let usage + let warnings + let rawResponse + let response + let request + let logprobs + let resultProviderMetadata switch (mode) { case "json": { const standardizedPrompt = standardizePrompt({ @@ -2357,12 +2361,12 @@ async function generateObject({ messages }, tools: void 0 - }); + }) const promptMessages = await convertToLanguageModelPrompt({ prompt: standardizedPrompt, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); + }) const generateResult = await retry( () => recordSpan({ name: "ai.generateObject.doGenerate", @@ -2394,7 +2398,7 @@ async function generateObject({ }), tracer, fn: async (span2) => { - var _a15, _b2, _c, _d, _e, _f; + var _a15, _b2, _c, _d, _e, _f const result2 = await model.doGenerate({ mode: { type: "object-json", @@ -2408,18 +2412,18 @@ async function generateObject({ providerMetadata, abortSignal, headers - }); + }) const responseData = { id: (_b2 = (_a15 = result2.response) == null ? void 0 : _a15.id) != null ? _b2 : generateId3(), timestamp: (_d = (_c = result2.response) == null ? void 0 : _c.timestamp) != null ? _d : currentDate(), modelId: (_f = (_e = result2.response) == null ? void 0 : _e.modelId) != null ? _f : model.modelId - }; + } if (result2.text === void 0) { throw new NoObjectGeneratedError({ message: "No object generated: the model did not return a response.", response: responseData, usage: calculateLanguageModelUsage(result2.usage) - }); + }) } span2.setAttributes( selectTelemetryAttributes({ @@ -2440,33 +2444,33 @@ async function generateObject({ "gen_ai.usage.completion_tokens": result2.usage.completionTokens } }) - ); - return { ...result2, objectText: result2.text, responseData }; + ) + return { ...result2, objectText: result2.text, responseData } } }) - ); - result = generateResult.objectText; - finishReason = generateResult.finishReason; - usage = generateResult.usage; - warnings = generateResult.warnings; - rawResponse = generateResult.rawResponse; - logprobs = generateResult.logprobs; - resultProviderMetadata = generateResult.providerMetadata; - request = (_a14 = generateResult.request) != null ? _a14 : {}; - response = generateResult.responseData; - break; + ) + result = generateResult.objectText + finishReason = generateResult.finishReason + usage = generateResult.usage + warnings = generateResult.warnings + rawResponse = generateResult.rawResponse + logprobs = generateResult.logprobs + resultProviderMetadata = generateResult.providerMetadata + request = (_a14 = generateResult.request) != null ? _a14 : {} + response = generateResult.responseData + break } case "tool": { const standardizedPrompt = standardizePrompt({ prompt: { system, prompt, messages }, tools: void 0 - }); + }) const promptMessages = await convertToLanguageModelPrompt({ prompt: standardizedPrompt, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); - const inputFormat = standardizedPrompt.type; + }) + const inputFormat = standardizedPrompt.type const generateResult = await retry( () => recordSpan({ name: "ai.generateObject.doGenerate", @@ -2498,7 +2502,7 @@ async function generateObject({ }), tracer, fn: async (span2) => { - var _a15, _b2, _c, _d, _e, _f, _g, _h; + var _a15, _b2, _c, _d, _e, _f, _g, _h const result2 = await model.doGenerate({ mode: { type: "object-tool", @@ -2515,19 +2519,19 @@ async function generateObject({ providerMetadata, abortSignal, headers - }); - const objectText = (_b2 = (_a15 = result2.toolCalls) == null ? void 0 : _a15[0]) == null ? void 0 : _b2.args; + }) + const objectText = (_b2 = (_a15 = result2.toolCalls) == null ? void 0 : _a15[0]) == null ? void 0 : _b2.args const responseData = { id: (_d = (_c = result2.response) == null ? void 0 : _c.id) != null ? _d : generateId3(), timestamp: (_f = (_e = result2.response) == null ? void 0 : _e.timestamp) != null ? _f : currentDate(), modelId: (_h = (_g = result2.response) == null ? void 0 : _g.modelId) != null ? _h : model.modelId - }; + } if (objectText === void 0) { throw new NoObjectGeneratedError({ message: "No object generated: the tool was not called.", response: responseData, usage: calculateLanguageModelUsage(result2.usage) - }); + }) } span2.setAttributes( selectTelemetryAttributes({ @@ -2548,33 +2552,33 @@ async function generateObject({ "gen_ai.usage.output_tokens": result2.usage.completionTokens } }) - ); - return { ...result2, objectText, responseData }; + ) + return { ...result2, objectText, responseData } } }) - ); - result = generateResult.objectText; - finishReason = generateResult.finishReason; - usage = generateResult.usage; - warnings = generateResult.warnings; - rawResponse = generateResult.rawResponse; - logprobs = generateResult.logprobs; - resultProviderMetadata = generateResult.providerMetadata; - request = (_b = generateResult.request) != null ? _b : {}; - response = generateResult.responseData; - break; + ) + result = generateResult.objectText + finishReason = generateResult.finishReason + usage = generateResult.usage + warnings = generateResult.warnings + rawResponse = generateResult.rawResponse + logprobs = generateResult.logprobs + resultProviderMetadata = generateResult.providerMetadata + request = (_b = generateResult.request) != null ? _b : {} + response = generateResult.responseData + break } case void 0: { throw new Error( "Model does not have a default object generation mode." - ); + ) } default: { - const _exhaustiveCheck = mode; - throw new Error(`Unsupported mode: ${_exhaustiveCheck}`); + const _exhaustiveCheck = mode + throw new Error(`Unsupported mode: ${_exhaustiveCheck}`) } } - const parseResult = (0, import_provider_utils6.safeParseJSON)({ text: result }); + const parseResult = (0, import_provider_utils6.safeParseJSON)({ text: result }) if (!parseResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: could not parse the response.", @@ -2582,7 +2586,7 @@ async function generateObject({ text: result, response, usage: calculateLanguageModelUsage(usage) - }); + }) } const validationResult = outputStrategy.validateFinalResult( parseResult.value, @@ -2591,7 +2595,7 @@ async function generateObject({ response, usage: calculateLanguageModelUsage(usage) } - ); + ) if (!validationResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: response did not match schema.", @@ -2599,7 +2603,7 @@ async function generateObject({ text: result, response, usage: calculateLanguageModelUsage(usage) - }); + }) } span.setAttributes( selectTelemetryAttributes({ @@ -2613,7 +2617,7 @@ async function generateObject({ "ai.usage.completionTokens": usage.completionTokens } }) - ); + ) return new DefaultGenerateObjectResult({ object: validationResult.value, finishReason, @@ -2626,155 +2630,155 @@ async function generateObject({ }, logprobs, providerMetadata: resultProviderMetadata - }); + }) } - }); + }) } var DefaultGenerateObjectResult = class { constructor(options) { - this.object = options.object; - this.finishReason = options.finishReason; - this.usage = options.usage; - this.warnings = options.warnings; - this.experimental_providerMetadata = options.providerMetadata; - this.response = options.response; - this.request = options.request; - this.logprobs = options.logprobs; + this.object = options.object + this.finishReason = options.finishReason + this.usage = options.usage + this.warnings = options.warnings + this.experimental_providerMetadata = options.providerMetadata + this.response = options.response + this.request = options.request + this.logprobs = options.logprobs } toJsonResponse(init) { - var _a14; + var _a14 return new Response(JSON.stringify(this.object), { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, headers: prepareResponseHeaders(init == null ? void 0 : init.headers, { contentType: "application/json; charset=utf-8" }) - }); + }) } -}; +} // core/generate-object/stream-object.ts -var import_provider_utils7 = require("@ai-sdk/provider-utils"); -var import_ui_utils3 = require("@ai-sdk/ui-utils"); +var import_provider_utils7 = require("@ai-sdk/provider-utils") +var import_ui_utils3 = require("@ai-sdk/ui-utils") // util/delayed-promise.ts var DelayedPromise = class { constructor() { - this.status = { type: "pending" }; - this._resolve = void 0; - this._reject = void 0; + this.status = { type: "pending" } + this._resolve = void 0 + this._reject = void 0 } get value() { if (this.promise) { - return this.promise; + return this.promise } this.promise = new Promise((resolve, reject) => { if (this.status.type === "resolved") { - resolve(this.status.value); + resolve(this.status.value) } else if (this.status.type === "rejected") { - reject(this.status.error); + reject(this.status.error) } - this._resolve = resolve; - this._reject = reject; - }); - return this.promise; + this._resolve = resolve + this._reject = reject + }) + return this.promise } resolve(value) { - var _a14; - this.status = { type: "resolved", value }; + var _a14 + this.status = { type: "resolved", value } if (this.promise) { - (_a14 = this._resolve) == null ? void 0 : _a14.call(this, value); + (_a14 = this._resolve) == null ? void 0 : _a14.call(this, value) } } reject(error) { - var _a14; - this.status = { type: "rejected", error }; + var _a14 + this.status = { type: "rejected", error } if (this.promise) { - (_a14 = this._reject) == null ? void 0 : _a14.call(this, error); + (_a14 = this._reject) == null ? void 0 : _a14.call(this, error) } } -}; +} // util/create-resolvable-promise.ts function createResolvablePromise() { - let resolve; - let reject; + let resolve + let reject const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); + resolve = res + reject = rej + }) return { promise, resolve, reject - }; + } } // core/util/create-stitchable-stream.ts function createStitchableStream() { - let innerStreamReaders = []; - let controller = null; - let isClosed = false; - let waitForNewStream = createResolvablePromise(); + let innerStreamReaders = [] + let controller = null + let isClosed = false + let waitForNewStream = createResolvablePromise() const processPull = async () => { if (isClosed && innerStreamReaders.length === 0) { - controller == null ? void 0 : controller.close(); - return; + controller == null ? void 0 : controller.close() + return } if (innerStreamReaders.length === 0) { - waitForNewStream = createResolvablePromise(); - await waitForNewStream.promise; - return processPull(); + waitForNewStream = createResolvablePromise() + await waitForNewStream.promise + return processPull() } try { - const { value, done } = await innerStreamReaders[0].read(); + const { value, done } = await innerStreamReaders[0].read() if (done) { - innerStreamReaders.shift(); + innerStreamReaders.shift() if (innerStreamReaders.length > 0) { - await processPull(); + await processPull() } else if (isClosed) { - controller == null ? void 0 : controller.close(); + controller == null ? void 0 : controller.close() } } else { - controller == null ? void 0 : controller.enqueue(value); + controller == null ? void 0 : controller.enqueue(value) } } catch (error) { - controller == null ? void 0 : controller.error(error); - innerStreamReaders.shift(); + controller == null ? void 0 : controller.error(error) + innerStreamReaders.shift() if (isClosed && innerStreamReaders.length === 0) { - controller == null ? void 0 : controller.close(); + controller == null ? void 0 : controller.close() } } - }; + } return { stream: new ReadableStream({ start(controllerParam) { - controller = controllerParam; + controller = controllerParam }, pull: processPull, async cancel() { for (const reader of innerStreamReaders) { - await reader.cancel(); + await reader.cancel() } - innerStreamReaders = []; - isClosed = true; + innerStreamReaders = [] + isClosed = true } }), addStream: (innerStream) => { if (isClosed) { - throw new Error("Cannot add inner stream: outer stream is closed"); + throw new Error("Cannot add inner stream: outer stream is closed") } - innerStreamReaders.push(innerStream.getReader()); - waitForNewStream.resolve(); + innerStreamReaders.push(innerStream.getReader()) + waitForNewStream.resolve() }, /** * Gracefully close the outer stream. This will let the inner streams * finish processing and then close the outer stream. */ close: () => { - isClosed = true; - waitForNewStream.resolve(); + isClosed = true + waitForNewStream.resolve() if (innerStreamReaders.length === 0) { - controller == null ? void 0 : controller.close(); + controller == null ? void 0 : controller.close() } }, /** @@ -2782,23 +2786,23 @@ function createStitchableStream() { * and close the outer stream. */ terminate: () => { - isClosed = true; - waitForNewStream.resolve(); - innerStreamReaders.forEach((reader) => reader.cancel()); - innerStreamReaders = []; - controller == null ? void 0 : controller.close(); + isClosed = true + waitForNewStream.resolve() + innerStreamReaders.forEach((reader) => reader.cancel()) + innerStreamReaders = [] + controller == null ? void 0 : controller.close() } - }; + } } // core/util/now.ts function now() { - var _a14, _b; - return (_b = (_a14 = globalThis == null ? void 0 : globalThis.performance) == null ? void 0 : _a14.now()) != null ? _b : Date.now(); + var _a14, _b + return (_b = (_a14 = globalThis == null ? void 0 : globalThis.performance) == null ? void 0 : _a14.now()) != null ? _b : Date.now() } // core/generate-object/stream-object.ts -var originalGenerateId2 = (0, import_provider_utils7.createIdGenerator)({ prefix: "aiobj", size: 24 }); +var originalGenerateId2 = (0, import_provider_utils7.createIdGenerator)({ prefix: "aiobj", size: 24 }) function streamObject({ model, schema: inputSchema, @@ -2828,10 +2832,10 @@ function streamObject({ schema: inputSchema, schemaName, schemaDescription - }); - const outputStrategy = getOutputStrategy({ output, schema: inputSchema }); + }) + const outputStrategy = getOutputStrategy({ output, schema: inputSchema }) if (outputStrategy.type === "no-schema" && mode === void 0) { - mode = "json"; + mode = "json" } return new DefaultStreamObjectResult({ model, @@ -2852,7 +2856,7 @@ function streamObject({ generateId: generateId3, currentDate, now: now2 - }); + }) } var DefaultStreamObjectResult = class { constructor({ @@ -2875,24 +2879,24 @@ var DefaultStreamObjectResult = class { currentDate, now: now2 }) { - this.objectPromise = new DelayedPromise(); - this.usagePromise = new DelayedPromise(); - this.providerMetadataPromise = new DelayedPromise(); - this.warningsPromise = new DelayedPromise(); - this.requestPromise = new DelayedPromise(); - this.responsePromise = new DelayedPromise(); - this.stitchableStream = createStitchableStream(); + this.objectPromise = new DelayedPromise() + this.usagePromise = new DelayedPromise() + this.providerMetadataPromise = new DelayedPromise() + this.warningsPromise = new DelayedPromise() + this.requestPromise = new DelayedPromise() + this.responsePromise = new DelayedPromise() + this.stitchableStream = createStitchableStream() const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg - }); + }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); - const tracer = getTracer(telemetry); - const self = this; + }) + const tracer = getTracer(telemetry) + const self = this recordSpan({ name: "ai.streamObject", attributes: selectTelemetryAttributes({ @@ -2918,10 +2922,10 @@ var DefaultStreamObjectResult = class { endWhenDone: false, fn: async (rootSpan) => { if (mode === "auto" || mode == null) { - mode = model.defaultObjectGenerationMode; + mode = model.defaultObjectGenerationMode } - let callOptions; - let transformer; + let callOptions + let transformer switch (mode) { case "json": { const standardizedPrompt = standardizePrompt({ @@ -2934,7 +2938,7 @@ var DefaultStreamObjectResult = class { messages }, tools: void 0 - }); + }) callOptions = { mode: { type: "object-json", @@ -2952,28 +2956,28 @@ var DefaultStreamObjectResult = class { providerMetadata: inputProviderMetadata, abortSignal, headers - }; + } transformer = { transform: (chunk, controller) => { switch (chunk.type) { case "text-delta": - controller.enqueue(chunk.textDelta); - break; + controller.enqueue(chunk.textDelta) + break case "response-metadata": case "finish": case "error": - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } } - }; - break; + } + break } case "tool": { const standardizedPrompt = standardizePrompt({ prompt: { system, prompt, messages }, tools: void 0 - }); + }) callOptions = { mode: { type: "object-tool", @@ -2994,31 +2998,31 @@ var DefaultStreamObjectResult = class { providerMetadata: inputProviderMetadata, abortSignal, headers - }; + } transformer = { transform(chunk, controller) { switch (chunk.type) { case "tool-call-delta": - controller.enqueue(chunk.argsTextDelta); - break; + controller.enqueue(chunk.argsTextDelta) + break case "response-metadata": case "finish": case "error": - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } } - }; - break; + } + break } case void 0: { throw new Error( "Model does not have a default object generation mode." - ); + ) } default: { - const _exhaustiveCheck = mode; - throw new Error(`Unsupported mode: ${_exhaustiveCheck}`); + const _exhaustiveCheck = mode + throw new Error(`Unsupported mode: ${_exhaustiveCheck}`) } } const { @@ -3062,42 +3066,42 @@ var DefaultStreamObjectResult = class { result: await model.doStream(callOptions) }) }) - ); - self.requestPromise.resolve(request != null ? request : {}); - let usage; - let finishReason; - let providerMetadata; - let object2; - let error; - let accumulatedText = ""; - let textDelta = ""; + ) + self.requestPromise.resolve(request != null ? request : {}) + let usage + let finishReason + let providerMetadata + let object2 + let error + let accumulatedText = "" + let textDelta = "" let response = { id: generateId3(), timestamp: currentDate(), modelId: model.modelId - }; - let latestObjectJson = void 0; - let latestObject = void 0; - let isFirstChunk = true; - let isFirstDelta = true; + } + let latestObjectJson = void 0 + let latestObject = void 0 + let isFirstChunk = true + let isFirstDelta = true const transformedStream = stream.pipeThrough(new TransformStream(transformer)).pipeThrough( new TransformStream({ async transform(chunk, controller) { - var _a14, _b, _c; + var _a14, _b, _c if (isFirstChunk) { - const msToFirstChunk = now2() - startTimestampMs; - isFirstChunk = false; + const msToFirstChunk = now2() - startTimestampMs + isFirstChunk = false doStreamSpan.addEvent("ai.stream.firstChunk", { "ai.stream.msToFirstChunk": msToFirstChunk - }); + }) doStreamSpan.setAttributes({ "ai.stream.msToFirstChunk": msToFirstChunk - }); + }) } if (typeof chunk === "string") { - accumulatedText += chunk; - textDelta += chunk; - const { value: currentObjectJson, state: parseState } = (0, import_ui_utils3.parsePartialJson)(accumulatedText); + accumulatedText += chunk + textDelta += chunk + const { value: currentObjectJson, state: parseState } = (0, import_ui_utils3.parsePartialJson)(accumulatedText) if (currentObjectJson !== void 0 && !(0, import_ui_utils3.isDeepEqualData)(latestObjectJson, currentObjectJson)) { const validationResult = outputStrategy.validatePartialResult({ value: currentObjectJson, @@ -3105,26 +3109,26 @@ var DefaultStreamObjectResult = class { latestObject, isFirstDelta, isFinalDelta: parseState === "successful-parse" - }); + }) if (validationResult.success && !(0, import_ui_utils3.isDeepEqualData)( latestObject, validationResult.value.partial )) { - latestObjectJson = currentObjectJson; - latestObject = validationResult.value.partial; + latestObjectJson = currentObjectJson + latestObject = validationResult.value.partial controller.enqueue({ type: "object", object: latestObject - }); + }) controller.enqueue({ type: "text-delta", textDelta: validationResult.value.textDelta - }); - textDelta = ""; - isFirstDelta = false; + }) + textDelta = "" + isFirstDelta = false } } - return; + return } switch (chunk.type) { case "response-metadata": { @@ -3132,23 +3136,23 @@ var DefaultStreamObjectResult = class { id: (_a14 = chunk.id) != null ? _a14 : response.id, timestamp: (_b = chunk.timestamp) != null ? _b : response.timestamp, modelId: (_c = chunk.modelId) != null ? _c : response.modelId - }; - break; + } + break } case "finish": { if (textDelta !== "") { - controller.enqueue({ type: "text-delta", textDelta }); + controller.enqueue({ type: "text-delta", textDelta }) } - finishReason = chunk.finishReason; - usage = calculateLanguageModelUsage(chunk.usage); - providerMetadata = chunk.providerMetadata; - controller.enqueue({ ...chunk, usage, response }); - self.usagePromise.resolve(usage); - self.providerMetadataPromise.resolve(providerMetadata); + finishReason = chunk.finishReason + usage = calculateLanguageModelUsage(chunk.usage) + providerMetadata = chunk.providerMetadata + controller.enqueue({ ...chunk, usage, response }) + self.usagePromise.resolve(usage) + self.providerMetadataPromise.resolve(providerMetadata) self.responsePromise.resolve({ ...response, headers: rawResponse == null ? void 0 : rawResponse.headers - }); + }) const validationResult = outputStrategy.validateFinalResult( latestObjectJson, { @@ -3156,10 +3160,10 @@ var DefaultStreamObjectResult = class { response, usage } - ); + ) if (validationResult.success) { - object2 = validationResult.value; - self.objectPromise.resolve(object2); + object2 = validationResult.value + self.objectPromise.resolve(object2) } else { error = new NoObjectGeneratedError({ message: "No object generated: response did not match schema.", @@ -3167,14 +3171,14 @@ var DefaultStreamObjectResult = class { text: accumulatedText, response, usage - }); - self.objectPromise.reject(error); + }) + self.objectPromise.reject(error) } - break; + break } default: { - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } } }, @@ -3185,7 +3189,7 @@ var DefaultStreamObjectResult = class { promptTokens: NaN, completionTokens: NaN, totalTokens: NaN - }; + } doStreamSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3207,8 +3211,8 @@ var DefaultStreamObjectResult = class { "gen_ai.usage.output_tokens": finalUsage.completionTokens } }) - ); - doStreamSpan.end(); + ) + doStreamSpan.end() rootSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3220,7 +3224,7 @@ var DefaultStreamObjectResult = class { } } }) - ); + ) await (onFinish == null ? void 0 : onFinish({ usage: finalUsage, object: object2, @@ -3231,47 +3235,47 @@ var DefaultStreamObjectResult = class { }, warnings, experimental_providerMetadata: providerMetadata - })); + })) } catch (error2) { - controller.error(error2); + controller.error(error2) } finally { - rootSpan.end(); + rootSpan.end() } } }) - ); - self.stitchableStream.addStream(transformedStream); + ) + self.stitchableStream.addStream(transformedStream) } }).catch((error) => { self.stitchableStream.addStream( new ReadableStream({ start(controller) { - controller.error(error); + controller.error(error) } }) - ); + ) }).finally(() => { - self.stitchableStream.close(); - }); - this.outputStrategy = outputStrategy; + self.stitchableStream.close() + }) + this.outputStrategy = outputStrategy } get object() { - return this.objectPromise.value; + return this.objectPromise.value } get usage() { - return this.usagePromise.value; + return this.usagePromise.value } get experimental_providerMetadata() { - return this.providerMetadataPromise.value; + return this.providerMetadataPromise.value } get warnings() { - return this.warningsPromise.value; + return this.warningsPromise.value } get request() { - return this.requestPromise.value; + return this.requestPromise.value } get response() { - return this.responsePromise.value; + return this.responsePromise.value } get partialObjectStream() { return createAsyncIterableStream( @@ -3280,28 +3284,28 @@ var DefaultStreamObjectResult = class { transform(chunk, controller) { switch (chunk.type) { case "object": - controller.enqueue(chunk.object); - break; + controller.enqueue(chunk.object) + break case "text-delta": case "finish": - break; + break case "error": - controller.error(chunk.error); - break; + controller.error(chunk.error) + break default: { - const _exhaustiveCheck = chunk; - throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`); + const _exhaustiveCheck = chunk + throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`) } } } }) ) - ); + ) } get elementStream() { return this.outputStrategy.createElementStream( this.stitchableStream.stream - ); + ) } get textStream() { return createAsyncIterableStream( @@ -3310,26 +3314,26 @@ var DefaultStreamObjectResult = class { transform(chunk, controller) { switch (chunk.type) { case "text-delta": - controller.enqueue(chunk.textDelta); - break; + controller.enqueue(chunk.textDelta) + break case "object": case "finish": - break; + break case "error": - controller.error(chunk.error); - break; + controller.error(chunk.error) + break default: { - const _exhaustiveCheck = chunk; - throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`); + const _exhaustiveCheck = chunk + throw new Error(`Unsupported chunk type: ${_exhaustiveCheck}`) } } } }) ) - ); + ) } get fullStream() { - return createAsyncIterableStream(this.stitchableStream.stream); + return createAsyncIterableStream(this.stitchableStream.stream) } pipeTextStreamToResponse(response, init) { writeToServerResponse({ @@ -3340,46 +3344,46 @@ var DefaultStreamObjectResult = class { contentType: "text/plain; charset=utf-8" }), stream: this.textStream.pipeThrough(new TextEncoderStream()) - }); + }) } toTextStreamResponse(init) { - var _a14; + var _a14 return new Response(this.textStream.pipeThrough(new TextEncoderStream()), { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, headers: prepareResponseHeaders(init == null ? void 0 : init.headers, { contentType: "text/plain; charset=utf-8" }) - }); + }) } -}; +} // core/generate-text/generate-text.ts -var import_provider_utils9 = require("@ai-sdk/provider-utils"); +var import_provider_utils9 = require("@ai-sdk/provider-utils") // errors/no-output-specified-error.ts -var import_provider11 = require("@ai-sdk/provider"); -var name8 = "AI_NoOutputSpecifiedError"; -var marker8 = `vercel.ai.error.${name8}`; -var symbol8 = Symbol.for(marker8); -var _a8; +var import_provider11 = require("@ai-sdk/provider") +var name8 = "AI_NoOutputSpecifiedError" +var marker8 = `vercel.ai.error.${name8}` +var symbol8 = Symbol.for(marker8) +var _a8 var NoOutputSpecifiedError = class extends import_provider11.AISDKError { // used in isInstance constructor({ message = "No output specified." } = {}) { - super({ name: name8, message }); - this[_a8] = true; + super({ name: name8, message }) + this[_a8] = true } static isInstance(error) { - return import_provider11.AISDKError.hasMarker(error, marker8); + return import_provider11.AISDKError.hasMarker(error, marker8) } -}; -_a8 = symbol8; +} +_a8 = symbol8 // errors/tool-execution-error.ts -var import_provider12 = require("@ai-sdk/provider"); -var name9 = "AI_ToolExecutionError"; -var marker9 = `vercel.ai.error.${name9}`; -var symbol9 = Symbol.for(marker9); -var _a9; +var import_provider12 = require("@ai-sdk/provider") +var name9 = "AI_ToolExecutionError" +var marker9 = `vercel.ai.error.${name9}` +var symbol9 = Symbol.for(marker9) +var _a9 var ToolExecutionError = class extends import_provider12.AISDKError { constructor({ toolArgs, @@ -3388,24 +3392,24 @@ var ToolExecutionError = class extends import_provider12.AISDKError { cause, message = `Error executing tool ${toolName}: ${(0, import_provider12.getErrorMessage)(cause)}` }) { - super({ name: name9, message, cause }); - this[_a9] = true; - this.toolArgs = toolArgs; - this.toolName = toolName; - this.toolCallId = toolCallId; + super({ name: name9, message, cause }) + this[_a9] = true + this.toolArgs = toolArgs + this.toolName = toolName + this.toolCallId = toolCallId } static isInstance(error) { - return import_provider12.AISDKError.hasMarker(error, marker9); + return import_provider12.AISDKError.hasMarker(error, marker9) } -}; -_a9 = symbol9; +} +_a9 = symbol9 // core/prompt/prepare-tools-and-tool-choice.ts -var import_ui_utils4 = require("@ai-sdk/ui-utils"); +var import_ui_utils4 = require("@ai-sdk/ui-utils") // core/util/is-non-empty-object.ts function isNonEmptyObject(object2) { - return object2 != null && Object.keys(object2).length > 0; + return object2 != null && Object.keys(object2).length > 0 } // core/prompt/prepare-tools-and-tool-choice.ts @@ -3418,14 +3422,14 @@ function prepareToolsAndToolChoice({ return { tools: void 0, toolChoice: void 0 - }; + } } const filteredTools = activeTools != null ? Object.entries(tools).filter( ([name14]) => activeTools.includes(name14) - ) : Object.entries(tools); + ) : Object.entries(tools) return { tools: filteredTools.map(([name14, tool2]) => { - const toolType = tool2.type; + const toolType = tool2.type switch (toolType) { case void 0: case "function": @@ -3434,47 +3438,47 @@ function prepareToolsAndToolChoice({ name: name14, description: tool2.description, parameters: (0, import_ui_utils4.asSchema)(tool2.parameters).jsonSchema - }; + } case "provider-defined": return { type: "provider-defined", name: name14, id: tool2.id, args: tool2.args - }; + } default: { - const exhaustiveCheck = toolType; - throw new Error(`Unsupported tool type: ${exhaustiveCheck}`); + const exhaustiveCheck = toolType + throw new Error(`Unsupported tool type: ${exhaustiveCheck}`) } } }), toolChoice: toolChoice == null ? { type: "auto" } : typeof toolChoice === "string" ? { type: toolChoice } : { type: "tool", toolName: toolChoice.toolName } - }; + } } // core/util/split-on-last-whitespace.ts -var lastWhitespaceRegexp = /^([\s\S]*?)(\s+)(\S*)$/; +var lastWhitespaceRegexp = /^([\s\S]*?)(\s+)(\S*)$/ function splitOnLastWhitespace(text2) { - const match = text2.match(lastWhitespaceRegexp); - return match ? { prefix: match[1], whitespace: match[2], suffix: match[3] } : void 0; + const match = text2.match(lastWhitespaceRegexp) + return match ? { prefix: match[1], whitespace: match[2], suffix: match[3] } : void 0 } // core/util/remove-text-after-last-whitespace.ts function removeTextAfterLastWhitespace(text2) { - const match = splitOnLastWhitespace(text2); - return match ? match.prefix + match.whitespace : text2; + const match = splitOnLastWhitespace(text2) + return match ? match.prefix + match.whitespace : text2 } // core/generate-text/parse-tool-call.ts -var import_provider_utils8 = require("@ai-sdk/provider-utils"); -var import_ui_utils5 = require("@ai-sdk/ui-utils"); +var import_provider_utils8 = require("@ai-sdk/provider-utils") +var import_ui_utils5 = require("@ai-sdk/ui-utils") // errors/invalid-tool-arguments-error.ts -var import_provider13 = require("@ai-sdk/provider"); -var name10 = "AI_InvalidToolArgumentsError"; -var marker10 = `vercel.ai.error.${name10}`; -var symbol10 = Symbol.for(marker10); -var _a10; +var import_provider13 = require("@ai-sdk/provider") +var name10 = "AI_InvalidToolArgumentsError" +var marker10 = `vercel.ai.error.${name10}` +var symbol10 = Symbol.for(marker10) +var _a10 var InvalidToolArgumentsError = class extends import_provider13.AISDKError { constructor({ toolArgs, @@ -3484,61 +3488,61 @@ var InvalidToolArgumentsError = class extends import_provider13.AISDKError { cause )}` }) { - super({ name: name10, message, cause }); - this[_a10] = true; - this.toolArgs = toolArgs; - this.toolName = toolName; + super({ name: name10, message, cause }) + this[_a10] = true + this.toolArgs = toolArgs + this.toolName = toolName } static isInstance(error) { - return import_provider13.AISDKError.hasMarker(error, marker10); + return import_provider13.AISDKError.hasMarker(error, marker10) } -}; -_a10 = symbol10; +} +_a10 = symbol10 // errors/no-such-tool-error.ts -var import_provider14 = require("@ai-sdk/provider"); -var name11 = "AI_NoSuchToolError"; -var marker11 = `vercel.ai.error.${name11}`; -var symbol11 = Symbol.for(marker11); -var _a11; +var import_provider14 = require("@ai-sdk/provider") +var name11 = "AI_NoSuchToolError" +var marker11 = `vercel.ai.error.${name11}` +var symbol11 = Symbol.for(marker11) +var _a11 var NoSuchToolError = class extends import_provider14.AISDKError { constructor({ toolName, availableTools = void 0, message = `Model tried to call unavailable tool '${toolName}'. ${availableTools === void 0 ? "No tools are available." : `Available tools: ${availableTools.join(", ")}.`}` }) { - super({ name: name11, message }); - this[_a11] = true; - this.toolName = toolName; - this.availableTools = availableTools; + super({ name: name11, message }) + this[_a11] = true + this.toolName = toolName + this.availableTools = availableTools } static isInstance(error) { - return import_provider14.AISDKError.hasMarker(error, marker11); + return import_provider14.AISDKError.hasMarker(error, marker11) } -}; -_a11 = symbol11; +} +_a11 = symbol11 // errors/tool-call-repair-error.ts -var import_provider15 = require("@ai-sdk/provider"); -var name12 = "AI_ToolCallRepairError"; -var marker12 = `vercel.ai.error.${name12}`; -var symbol12 = Symbol.for(marker12); -var _a12; +var import_provider15 = require("@ai-sdk/provider") +var name12 = "AI_ToolCallRepairError" +var marker12 = `vercel.ai.error.${name12}` +var symbol12 = Symbol.for(marker12) +var _a12 var ToolCallRepairError = class extends import_provider15.AISDKError { constructor({ cause, originalError, message = `Error repairing tool call: ${(0, import_provider15.getErrorMessage)(cause)}` }) { - super({ name: name12, message, cause }); - this[_a12] = true; - this.originalError = originalError; + super({ name: name12, message, cause }) + this[_a12] = true + this.originalError = originalError } static isInstance(error) { - return import_provider15.AISDKError.hasMarker(error, marker12); + return import_provider15.AISDKError.hasMarker(error, marker12) } -}; -_a12 = symbol12; +} +_a12 = symbol12 // core/generate-text/parse-tool-call.ts async function parseToolCall({ @@ -3549,15 +3553,15 @@ async function parseToolCall({ messages }) { if (tools == null) { - throw new NoSuchToolError({ toolName: toolCall.toolName }); + throw new NoSuchToolError({ toolName: toolCall.toolName }) } try { - return await doParseToolCall({ toolCall, tools }); + return await doParseToolCall({ toolCall, tools }) } catch (error) { if (repairToolCall == null || !(NoSuchToolError.isInstance(error) || InvalidToolArgumentsError.isInstance(error))) { - throw error; + throw error } - let repairedToolCall = null; + let repairedToolCall = null try { repairedToolCall = await repairToolCall({ toolCall, @@ -3566,46 +3570,46 @@ async function parseToolCall({ system, messages, error - }); + }) } catch (repairError) { throw new ToolCallRepairError({ cause: repairError, originalError: error - }); + }) } if (repairedToolCall == null) { - throw error; + throw error } - return await doParseToolCall({ toolCall: repairedToolCall, tools }); + return await doParseToolCall({ toolCall: repairedToolCall, tools }) } } async function doParseToolCall({ toolCall, tools }) { - const toolName = toolCall.toolName; - const tool2 = tools[toolName]; + const toolName = toolCall.toolName + const tool2 = tools[toolName] if (tool2 == null) { throw new NoSuchToolError({ toolName: toolCall.toolName, availableTools: Object.keys(tools) - }); + }) } - const schema = (0, import_ui_utils5.asSchema)(tool2.parameters); - const parseResult = toolCall.args.trim() === "" ? (0, import_provider_utils8.safeValidateTypes)({ value: {}, schema }) : (0, import_provider_utils8.safeParseJSON)({ text: toolCall.args, schema }); + const schema = (0, import_ui_utils5.asSchema)(tool2.parameters) + const parseResult = toolCall.args.trim() === "" ? (0, import_provider_utils8.safeValidateTypes)({ value: {}, schema }) : (0, import_provider_utils8.safeParseJSON)({ text: toolCall.args, schema }) if (parseResult.success === false) { throw new InvalidToolArgumentsError({ toolName, toolArgs: toolCall.args, cause: parseResult.error - }); + }) } return { type: "tool-call", toolCallId: toolCall.toolCallId, toolName, args: parseResult.value - }; + } } // core/generate-text/to-response-messages.ts @@ -3617,18 +3621,18 @@ function toResponseMessages({ messageId, generateMessageId }) { - const responseMessages = []; + const responseMessages = [] responseMessages.push({ role: "assistant", content: [{ type: "text", text: text2 }, ...toolCalls], id: messageId - }); + }) if (toolResults.length > 0) { responseMessages.push({ role: "tool", id: generateMessageId(), content: toolResults.map((toolResult) => { - const tool2 = tools[toolResult.toolName]; + const tool2 = tools[toolResult.toolName] return (tool2 == null ? void 0 : tool2.experimental_toToolResultContent) != null ? { type: "tool-result", toolCallId: toolResult.toolCallId, @@ -3642,22 +3646,22 @@ function toResponseMessages({ toolCallId: toolResult.toolCallId, toolName: toolResult.toolName, result: toolResult.result - }; + } }) - }); + }) } - return responseMessages; + return responseMessages } // core/generate-text/generate-text.ts var originalGenerateId3 = (0, import_provider_utils9.createIdGenerator)({ prefix: "aitxt", size: 24 -}); +}) var originalGenerateMessageId = (0, import_provider_utils9.createIdGenerator)({ prefix: "msg", size: 24 -}); +}) async function generateText({ model, tools, @@ -3683,21 +3687,21 @@ async function generateText({ onStepFinish, ...settings }) { - var _a14; + var _a14 if (maxSteps < 1) { throw new InvalidArgumentError({ parameter: "maxSteps", value: maxSteps, message: "maxSteps must be at least 1" - }); + }) } - const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }); + const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg }) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); + }) const initialPrompt = standardizePrompt({ prompt: { system: (_a14 = output == null ? void 0 : output.injectIntoSystemPrompt({ system, model })) != null ? _a14 : system, @@ -3705,8 +3709,8 @@ async function generateText({ messages }, tools - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) return recordSpan({ name: "ai.generateText", attributes: selectTelemetryAttributes({ @@ -3726,31 +3730,31 @@ async function generateText({ }), tracer, fn: async (span) => { - var _a15, _b, _c, _d, _e, _f; + var _a15, _b, _c, _d, _e, _f const mode = { type: "regular", ...prepareToolsAndToolChoice({ tools, toolChoice, activeTools }) - }; - const callSettings = prepareCallSettings(settings); - let currentModelResponse; - let currentToolCalls = []; - let currentToolResults = []; - let stepCount = 0; - const responseMessages = []; - let text2 = ""; - const steps = []; + } + const callSettings = prepareCallSettings(settings) + let currentModelResponse + let currentToolCalls = [] + let currentToolResults = [] + let stepCount = 0 + const responseMessages = [] + let text2 = "" + const steps = [] let usage = { completionTokens: 0, promptTokens: 0, totalTokens: 0 - }; - let stepType = "initial"; + } + let stepType = "initial" do { - const promptFormat = stepCount === 0 ? initialPrompt.type : "messages"; + const promptFormat = stepCount === 0 ? initialPrompt.type : "messages" const stepInputMessages = [ ...initialPrompt.messages, ...responseMessages - ]; + ] const promptMessages = await convertToLanguageModelPrompt({ prompt: { type: promptFormat, @@ -3759,7 +3763,7 @@ async function generateText({ }, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); + }) currentModelResponse = await retry( () => recordSpan({ name: "ai.generateText.doGenerate", @@ -3778,8 +3782,8 @@ async function generateText({ "ai.prompt.tools": { // convert the language model level tools: input: () => { - var _a16; - return (_a16 = mode.tools) == null ? void 0 : _a16.map((tool2) => JSON.stringify(tool2)); + var _a16 + return (_a16 = mode.tools) == null ? void 0 : _a16.map((tool2) => JSON.stringify(tool2)) } }, "ai.prompt.toolChoice": { @@ -3799,7 +3803,7 @@ async function generateText({ }), tracer, fn: async (span2) => { - var _a16, _b2, _c2, _d2, _e2, _f2; + var _a16, _b2, _c2, _d2, _e2, _f2 const result = await model.doGenerate({ mode, ...callSettings, @@ -3809,12 +3813,12 @@ async function generateText({ providerMetadata, abortSignal, headers - }); + }) const responseData = { id: (_b2 = (_a16 = result.response) == null ? void 0 : _a16.id) != null ? _b2 : generateId3(), timestamp: (_d2 = (_c2 = result.response) == null ? void 0 : _c2.timestamp) != null ? _d2 : currentDate(), modelId: (_f2 = (_e2 = result.response) == null ? void 0 : _e2.modelId) != null ? _f2 : model.modelId - }; + } span2.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3839,11 +3843,11 @@ async function generateText({ "gen_ai.usage.output_tokens": result.usage.completionTokens } }) - ); - return { ...result, response: responseData }; + ) + return { ...result, response: responseData } } }) - ); + ) currentToolCalls = await Promise.all( ((_a15 = currentModelResponse.toolCalls) != null ? _a15 : []).map( (toolCall) => parseToolCall({ @@ -3854,7 +3858,7 @@ async function generateText({ messages: stepInputMessages }) ) - ); + ) currentToolResults = tools == null ? [] : await executeTools({ toolCalls: currentToolCalls, tools, @@ -3862,38 +3866,38 @@ async function generateText({ telemetry, messages: stepInputMessages, abortSignal - }); + }) const currentUsage = calculateLanguageModelUsage( currentModelResponse.usage - ); - usage = addLanguageModelUsage(usage, currentUsage); - let nextStepType = "done"; + ) + usage = addLanguageModelUsage(usage, currentUsage) + let nextStepType = "done" if (++stepCount < maxSteps) { if (continueSteps && currentModelResponse.finishReason === "length" && // only use continue when there are no tool calls: currentToolCalls.length === 0) { - nextStepType = "continue"; + nextStepType = "continue" } else if ( // there are tool calls: currentToolCalls.length > 0 && // all current tool calls have results: currentToolResults.length === currentToolCalls.length ) { - nextStepType = "tool-result"; + nextStepType = "tool-result" } } - const originalText = (_b = currentModelResponse.text) != null ? _b : ""; + const originalText = (_b = currentModelResponse.text) != null ? _b : "" const stepTextLeadingWhitespaceTrimmed = stepType === "continue" && // only for continue steps - text2.trimEnd() !== text2 ? originalText.trimStart() : originalText; - const stepText = nextStepType === "continue" ? removeTextAfterLastWhitespace(stepTextLeadingWhitespaceTrimmed) : stepTextLeadingWhitespaceTrimmed; - text2 = nextStepType === "continue" || stepType === "continue" ? text2 + stepText : stepText; + text2.trimEnd() !== text2 ? originalText.trimStart() : originalText + const stepText = nextStepType === "continue" ? removeTextAfterLastWhitespace(stepTextLeadingWhitespaceTrimmed) : stepTextLeadingWhitespaceTrimmed + text2 = nextStepType === "continue" || stepType === "continue" ? text2 + stepText : stepText if (stepType === "continue") { - const lastMessage = responseMessages[responseMessages.length - 1]; + const lastMessage = responseMessages[responseMessages.length - 1] if (typeof lastMessage.content === "string") { - lastMessage.content += stepText; + lastMessage.content += stepText } else { lastMessage.content.push({ text: stepText, type: "text" - }); + }) } } else { responseMessages.push( @@ -3905,7 +3909,7 @@ async function generateText({ messageId: generateMessageId(), generateMessageId }) - ); + ) } const currentStepResult = { stepType, @@ -3925,11 +3929,11 @@ async function generateText({ }, experimental_providerMetadata: currentModelResponse.providerMetadata, isContinued: nextStepType === "continue" - }; - steps.push(currentStepResult); - await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)); - stepType = nextStepType; - } while (stepType !== "done"); + } + steps.push(currentStepResult) + await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)) + stepType = nextStepType + } while (stepType !== "done") span.setAttributes( selectTelemetryAttributes({ telemetry, @@ -3945,17 +3949,17 @@ async function generateText({ "ai.usage.completionTokens": currentModelResponse.usage.completionTokens } }) - ); + ) return new DefaultGenerateTextResult({ text: text2, outputResolver: () => { if (output == null) { - throw new NoOutputSpecifiedError(); + throw new NoOutputSpecifiedError() } return output.parseOutput( { text: text2 }, { response: currentModelResponse.response, usage } - ); + ) }, toolCalls: currentToolCalls, toolResults: currentToolResults, @@ -3971,9 +3975,9 @@ async function generateText({ logprobs: currentModelResponse.logprobs, steps, providerMetadata: currentModelResponse.providerMetadata - }); + }) } - }); + }) } async function executeTools({ toolCalls, @@ -3985,9 +3989,9 @@ async function executeTools({ }) { const toolResults = await Promise.all( toolCalls.map(async ({ toolCallId, toolName, args }) => { - const tool2 = tools[toolName]; + const tool2 = tools[toolName] if ((tool2 == null ? void 0 : tool2.execute) == null) { - return void 0; + return void 0 } const result = await recordSpan({ name: "ai.toolCall", @@ -4012,7 +4016,7 @@ async function executeTools({ toolCallId, messages, abortSignal - }); + }) try { span.setAttributes( selectTelemetryAttributes({ @@ -4023,83 +4027,84 @@ async function executeTools({ } } }) - ); + ) } catch (ignored) { + console.error(ignored) } - return result2; + return result2 } catch (error) { throw new ToolExecutionError({ toolCallId, toolName, toolArgs: args, cause: error - }); + }) } } - }); + }) return { type: "tool-result", toolCallId, toolName, args, result - }; + } }) - ); + ) return toolResults.filter( (result) => result != null - ); + ) } var DefaultGenerateTextResult = class { constructor(options) { - this.text = options.text; - this.toolCalls = options.toolCalls; - this.toolResults = options.toolResults; - this.finishReason = options.finishReason; - this.usage = options.usage; - this.warnings = options.warnings; - this.request = options.request; - this.response = options.response; - this.steps = options.steps; - this.experimental_providerMetadata = options.providerMetadata; - this.logprobs = options.logprobs; - this.outputResolver = options.outputResolver; + this.text = options.text + this.toolCalls = options.toolCalls + this.toolResults = options.toolResults + this.finishReason = options.finishReason + this.usage = options.usage + this.warnings = options.warnings + this.request = options.request + this.response = options.response + this.steps = options.steps + this.experimental_providerMetadata = options.providerMetadata + this.logprobs = options.logprobs + this.outputResolver = options.outputResolver } get experimental_output() { - return this.outputResolver(); + return this.outputResolver() } -}; +} // core/generate-text/output.ts -var output_exports = {}; +var output_exports = {} __export(output_exports, { object: () => object, text: () => text -}); -var import_provider_utils10 = require("@ai-sdk/provider-utils"); -var import_ui_utils6 = require("@ai-sdk/ui-utils"); +}) +var import_provider_utils10 = require("@ai-sdk/provider-utils") +var import_ui_utils6 = require("@ai-sdk/ui-utils") // errors/index.ts -var import_provider16 = require("@ai-sdk/provider"); +var import_provider16 = require("@ai-sdk/provider") // core/generate-text/output.ts var text = () => ({ type: "text", responseFormat: () => ({ type: "text" }), injectIntoSystemPrompt({ system }) { - return system; + return system }, parsePartial({ text: text2 }) { - return { partial: text2 }; + return { partial: text2 } }, parseOutput({ text: text2 }) { - return text2; + return text2 } -}); +}) var object = ({ schema: inputSchema }) => { - const schema = (0, import_ui_utils6.asSchema)(inputSchema); + const schema = (0, import_ui_utils6.asSchema)(inputSchema) return { type: "object", responseFormat: ({ model }) => ({ @@ -4110,28 +4115,28 @@ var object = ({ return model.supportsStructuredOutputs ? system : injectJsonInstruction({ prompt: system, schema: schema.jsonSchema - }); + }) }, parsePartial({ text: text2 }) { - const result = (0, import_ui_utils6.parsePartialJson)(text2); + const result = (0, import_ui_utils6.parsePartialJson)(text2) switch (result.state) { case "failed-parse": case "undefined-input": - return void 0; + return void 0 case "repaired-parse": case "successful-parse": return { // Note: currently no validation of partial results: partial: result.value - }; + } default: { - const _exhaustiveCheck = result.state; - throw new Error(`Unsupported parse state: ${_exhaustiveCheck}`); + const _exhaustiveCheck = result.state + throw new Error(`Unsupported parse state: ${_exhaustiveCheck}`) } } }, parseOutput({ text: text2 }, context) { - const parseResult = (0, import_provider_utils10.safeParseJSON)({ text: text2 }); + const parseResult = (0, import_provider_utils10.safeParseJSON)({ text: text2 }) if (!parseResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: could not parse the response.", @@ -4139,12 +4144,12 @@ var object = ({ text: text2, response: context.response, usage: context.usage - }); + }) } const validationResult = (0, import_provider_utils10.safeValidateTypes)({ value: parseResult.value, schema - }); + }) if (!validationResult.success) { throw new NoObjectGeneratedError({ message: "No object generated: response did not match schema.", @@ -4152,159 +4157,159 @@ var object = ({ text: text2, response: context.response, usage: context.usage - }); + }) } - return validationResult.value; + return validationResult.value } - }; -}; + } +} // core/generate-text/smooth-stream.ts -var import_provider17 = require("@ai-sdk/provider"); +var import_provider17 = require("@ai-sdk/provider") var CHUNKING_REGEXPS = { word: /\s*\S+\s+/m, line: /[^\n]*\n/m -}; +} function smoothStream({ delayInMs = 10, chunking = "word", _internal: { delay: delay2 = delay } = {} } = {}) { - const chunkingRegexp = typeof chunking === "string" ? CHUNKING_REGEXPS[chunking] : chunking; + const chunkingRegexp = typeof chunking === "string" ? CHUNKING_REGEXPS[chunking] : chunking if (chunkingRegexp == null) { throw new import_provider17.InvalidArgumentError({ argument: "chunking", message: `Chunking must be "word" or "line" or a RegExp. Received: ${chunking}` - }); + }) } return () => { - let buffer = ""; + let buffer = "" return new TransformStream({ async transform(chunk, controller) { if (chunk.type === "step-finish") { if (buffer.length > 0) { - controller.enqueue({ type: "text-delta", textDelta: buffer }); - buffer = ""; + controller.enqueue({ type: "text-delta", textDelta: buffer }) + buffer = "" } - controller.enqueue(chunk); - return; + controller.enqueue(chunk) + return } if (chunk.type !== "text-delta") { - controller.enqueue(chunk); - return; + controller.enqueue(chunk) + return } - buffer += chunk.textDelta; - let match; + buffer += chunk.textDelta + let match while ((match = chunkingRegexp.exec(buffer)) != null) { - const chunk2 = match[0]; - controller.enqueue({ type: "text-delta", textDelta: chunk2 }); - buffer = buffer.slice(chunk2.length); - await delay2(delayInMs); + const chunk2 = match[0] + controller.enqueue({ type: "text-delta", textDelta: chunk2 }) + buffer = buffer.slice(chunk2.length) + await delay2(delayInMs) } } - }); - }; + }) + } } // core/generate-text/stream-text.ts -var import_provider_utils11 = require("@ai-sdk/provider-utils"); -var import_ui_utils8 = require("@ai-sdk/ui-utils"); +var import_provider_utils11 = require("@ai-sdk/provider-utils") +var import_ui_utils8 = require("@ai-sdk/ui-utils") // util/as-array.ts function asArray(value) { - return value === void 0 ? [] : Array.isArray(value) ? value : [value]; + return value === void 0 ? [] : Array.isArray(value) ? value : [value] } // core/util/merge-streams.ts function mergeStreams(stream1, stream2) { - const reader1 = stream1.getReader(); - const reader2 = stream2.getReader(); - let lastRead1 = void 0; - let lastRead2 = void 0; - let stream1Done = false; - let stream2Done = false; + const reader1 = stream1.getReader() + const reader2 = stream2.getReader() + let lastRead1 = void 0 + let lastRead2 = void 0 + let stream1Done = false + let stream2Done = false async function readStream1(controller) { try { if (lastRead1 == null) { - lastRead1 = reader1.read(); + lastRead1 = reader1.read() } - const result = await lastRead1; - lastRead1 = void 0; + const result = await lastRead1 + lastRead1 = void 0 if (!result.done) { - controller.enqueue(result.value); + controller.enqueue(result.value) } else { - controller.close(); + controller.close() } } catch (error) { - controller.error(error); + controller.error(error) } } async function readStream2(controller) { try { if (lastRead2 == null) { - lastRead2 = reader2.read(); + lastRead2 = reader2.read() } - const result = await lastRead2; - lastRead2 = void 0; + const result = await lastRead2 + lastRead2 = void 0 if (!result.done) { - controller.enqueue(result.value); + controller.enqueue(result.value) } else { - controller.close(); + controller.close() } } catch (error) { - controller.error(error); + controller.error(error) } } return new ReadableStream({ async pull(controller) { try { if (stream1Done) { - await readStream2(controller); - return; + await readStream2(controller) + return } if (stream2Done) { - await readStream1(controller); - return; + await readStream1(controller) + return } if (lastRead1 == null) { - lastRead1 = reader1.read(); + lastRead1 = reader1.read() } if (lastRead2 == null) { - lastRead2 = reader2.read(); + lastRead2 = reader2.read() } const { result, reader } = await Promise.race([ lastRead1.then((result2) => ({ result: result2, reader: reader1 })), lastRead2.then((result2) => ({ result: result2, reader: reader2 })) - ]); + ]) if (!result.done) { - controller.enqueue(result.value); + controller.enqueue(result.value) } if (reader === reader1) { - lastRead1 = void 0; + lastRead1 = void 0 if (result.done) { - await readStream2(controller); - stream1Done = true; + await readStream2(controller) + stream1Done = true } } else { - lastRead2 = void 0; + lastRead2 = void 0 if (result.done) { - stream2Done = true; - await readStream1(controller); + stream2Done = true + await readStream1(controller) } } } catch (error) { - controller.error(error); + controller.error(error) } }, cancel() { - reader1.cancel(); - reader2.cancel(); + reader1.cancel() + reader2.cancel() } - }); + }) } // core/generate-text/run-tools-transformation.ts -var import_ui_utils7 = require("@ai-sdk/ui-utils"); +var import_ui_utils7 = require("@ai-sdk/ui-utils") function runToolsTransformation({ tools, generatorStream, @@ -4316,33 +4321,33 @@ function runToolsTransformation({ abortSignal, repairToolCall }) { - let toolResultsStreamController = null; + let toolResultsStreamController = null const toolResultsStream = new ReadableStream({ start(controller) { - toolResultsStreamController = controller; + toolResultsStreamController = controller } - }); - const activeToolCalls = {}; - const outstandingToolResults = /* @__PURE__ */ new Set(); - let canClose = false; - let finishChunk = void 0; + }) + const activeToolCalls = {} + const outstandingToolResults = /* @__PURE__ */ new Set() + let canClose = false + let finishChunk = void 0 function attemptClose() { if (canClose && outstandingToolResults.size === 0) { if (finishChunk != null) { - toolResultsStreamController.enqueue(finishChunk); + toolResultsStreamController.enqueue(finishChunk) } - toolResultsStreamController.close(); + toolResultsStreamController.close() } } const forwardStream = new TransformStream({ async transform(chunk, controller) { - const chunkType = chunk.type; + const chunkType = chunk.type switch (chunkType) { case "text-delta": case "response-metadata": case "error": { - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } case "tool-call-delta": { if (toolCallStreaming) { @@ -4351,17 +4356,17 @@ function runToolsTransformation({ type: "tool-call-streaming-start", toolCallId: chunk.toolCallId, toolName: chunk.toolName - }); - activeToolCalls[chunk.toolCallId] = true; + }) + activeToolCalls[chunk.toolCallId] = true } controller.enqueue({ type: "tool-call-delta", toolCallId: chunk.toolCallId, toolName: chunk.toolName, argsTextDelta: chunk.argsTextDelta - }); + }) } - break; + break } case "tool-call": { try { @@ -4371,12 +4376,12 @@ function runToolsTransformation({ repairToolCall, system, messages - }); - controller.enqueue(toolCall); - const tool2 = tools[toolCall.toolName]; + }) + controller.enqueue(toolCall) + const tool2 = tools[toolCall.toolName] if (tool2.execute != null) { - const toolExecutionId = (0, import_ui_utils7.generateId)(); - outstandingToolResults.add(toolExecutionId); + const toolExecutionId = (0, import_ui_utils7.generateId)() + outstandingToolResults.add(toolExecutionId) recordSpan({ name: "ai.toolCall", attributes: selectTelemetryAttributes({ @@ -4404,9 +4409,9 @@ function runToolsTransformation({ ...toolCall, type: "tool-result", result - }); - outstandingToolResults.delete(toolExecutionId); - attemptClose(); + }) + outstandingToolResults.delete(toolExecutionId) + attemptClose() try { span.setAttributes( selectTelemetryAttributes({ @@ -4417,7 +4422,7 @@ function runToolsTransformation({ } } }) - ); + ) } catch (ignored) { } }, @@ -4430,20 +4435,20 @@ function runToolsTransformation({ toolArgs: toolCall.args, cause: error }) - }); - outstandingToolResults.delete(toolExecutionId); - attemptClose(); + }) + outstandingToolResults.delete(toolExecutionId) + attemptClose() } ) - }); + }) } } catch (error) { toolResultsStreamController.enqueue({ type: "error", error - }); + }) } - break; + break } case "finish": { finishChunk = { @@ -4452,27 +4457,27 @@ function runToolsTransformation({ logprobs: chunk.logprobs, usage: calculateLanguageModelUsage(chunk.usage), experimental_providerMetadata: chunk.providerMetadata - }; - break; + } + break } default: { - const _exhaustiveCheck = chunkType; - throw new Error(`Unhandled chunk type: ${_exhaustiveCheck}`); + const _exhaustiveCheck = chunkType + throw new Error(`Unhandled chunk type: ${_exhaustiveCheck}`) } } }, flush() { - canClose = true; - attemptClose(); + canClose = true + attemptClose() } - }); + }) return new ReadableStream({ async start(controller) { return Promise.all([ generatorStream.pipeThrough(forwardStream).pipeTo( new WritableStream({ write(chunk) { - controller.enqueue(chunk); + controller.enqueue(chunk) }, close() { } @@ -4481,27 +4486,27 @@ function runToolsTransformation({ toolResultsStream.pipeTo( new WritableStream({ write(chunk) { - controller.enqueue(chunk); + controller.enqueue(chunk) }, close() { - controller.close(); + controller.close() } }) ) - ]); + ]) } - }); + }) } // core/generate-text/stream-text.ts var originalGenerateId4 = (0, import_provider_utils11.createIdGenerator)({ prefix: "aitxt", size: 24 -}); +}) var originalGenerateMessageId2 = (0, import_provider_utils11.createIdGenerator)({ prefix: "msg", size: 24 -}); +}) function streamText({ model, tools, @@ -4559,33 +4564,33 @@ function streamText({ currentDate, generateId: generateId3, generateMessageId - }); + }) } function createOutputTransformStream(output) { if (!output) { return new TransformStream({ transform(chunk, controller) { - controller.enqueue({ part: chunk, partialOutput: void 0 }); + controller.enqueue({ part: chunk, partialOutput: void 0 }) } - }); + }) } - let text2 = ""; - let textChunk = ""; - let lastPublishedJson = ""; + let text2 = "" + let textChunk = "" + let lastPublishedJson = "" return new TransformStream({ transform(chunk, controller) { if (chunk.type !== "text-delta") { controller.enqueue({ part: chunk, partialOutput: void 0 - }); - return; + }) + return } - text2 += chunk.textDelta; - textChunk += chunk.textDelta; - const result = output.parsePartial({ text: text2 }); + text2 += chunk.textDelta + textChunk += chunk.textDelta + const result = output.parsePartial({ text: text2 }) if (result != null) { - const currentJson = JSON.stringify(result.partial); + const currentJson = JSON.stringify(result.partial) if (currentJson !== lastPublishedJson) { controller.enqueue({ part: { @@ -4593,9 +4598,9 @@ function createOutputTransformStream(output) { textDelta: textChunk }, partialOutput: result.partial - }); - lastPublishedJson = currentJson; - textChunk = ""; + }) + lastPublishedJson = currentJson + textChunk = "" } } }, @@ -4607,10 +4612,10 @@ function createOutputTransformStream(output) { textDelta: textChunk }, partialOutput: void 0 - }); + }) } } - }); + }) } var DefaultStreamTextResult = class { constructor({ @@ -4641,58 +4646,58 @@ var DefaultStreamTextResult = class { generateId: generateId3, generateMessageId }) { - this.warningsPromise = new DelayedPromise(); - this.usagePromise = new DelayedPromise(); - this.finishReasonPromise = new DelayedPromise(); - this.providerMetadataPromise = new DelayedPromise(); - this.textPromise = new DelayedPromise(); - this.toolCallsPromise = new DelayedPromise(); - this.toolResultsPromise = new DelayedPromise(); - this.requestPromise = new DelayedPromise(); - this.responsePromise = new DelayedPromise(); - this.stepsPromise = new DelayedPromise(); - var _a14; + this.warningsPromise = new DelayedPromise() + this.usagePromise = new DelayedPromise() + this.finishReasonPromise = new DelayedPromise() + this.providerMetadataPromise = new DelayedPromise() + this.textPromise = new DelayedPromise() + this.toolCallsPromise = new DelayedPromise() + this.toolResultsPromise = new DelayedPromise() + this.requestPromise = new DelayedPromise() + this.responsePromise = new DelayedPromise() + this.stepsPromise = new DelayedPromise() + var _a14 if (maxSteps < 1) { throw new InvalidArgumentError({ parameter: "maxSteps", value: maxSteps, message: "maxSteps must be at least 1" - }); + }) } - this.output = output; - let recordedStepText = ""; - let recordedContinuationText = ""; - let recordedFullText = ""; + this.output = output + let recordedStepText = "" + let recordedContinuationText = "" + let recordedFullText = "" const recordedResponse = { id: generateId3(), timestamp: currentDate(), modelId: model.modelId, messages: [] - }; - let recordedToolCalls = []; - let recordedToolResults = []; - let recordedFinishReason = void 0; - let recordedUsage = void 0; - let stepType = "initial"; - const recordedSteps = []; - let rootSpan; + } + let recordedToolCalls = [] + let recordedToolResults = [] + let recordedFinishReason = void 0 + let recordedUsage = void 0 + let stepType = "initial" + const recordedSteps = [] + let rootSpan const eventProcessor = new TransformStream({ async transform(chunk, controller) { - controller.enqueue(chunk); - const { part } = chunk; + controller.enqueue(chunk) + const { part } = chunk if (part.type === "text-delta" || part.type === "tool-call" || part.type === "tool-result" || part.type === "tool-call-streaming-start" || part.type === "tool-call-delta") { - await (onChunk == null ? void 0 : onChunk({ chunk: part })); + await (onChunk == null ? void 0 : onChunk({ chunk: part })) } if (part.type === "text-delta") { - recordedStepText += part.textDelta; - recordedContinuationText += part.textDelta; - recordedFullText += part.textDelta; + recordedStepText += part.textDelta + recordedContinuationText += part.textDelta + recordedFullText += part.textDelta } if (part.type === "tool-call") { - recordedToolCalls.push(part); + recordedToolCalls.push(part) } if (part.type === "tool-result") { - recordedToolResults.push(part); + recordedToolResults.push(part) } if (part.type === "step-finish") { const stepMessages = toResponseMessages({ @@ -4702,19 +4707,19 @@ var DefaultStreamTextResult = class { toolResults: recordedToolResults, messageId: part.messageId, generateMessageId - }); - const currentStep = recordedSteps.length; - let nextStepType = "done"; + }) + const currentStep = recordedSteps.length + let nextStepType = "done" if (currentStep + 1 < maxSteps) { if (continueSteps && part.finishReason === "length" && // only use continue when there are no tool calls: recordedToolCalls.length === 0) { - nextStepType = "continue"; + nextStepType = "continue" } else if ( // there are tool calls: recordedToolCalls.length > 0 && // all current tool calls have results: recordedToolResults.length === recordedToolCalls.length ) { - nextStepType = "tool-result"; + nextStepType = "tool-result" } } const currentStepResult = { @@ -4733,54 +4738,54 @@ var DefaultStreamTextResult = class { }, experimental_providerMetadata: part.experimental_providerMetadata, isContinued: part.isContinued - }; - await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)); - recordedSteps.push(currentStepResult); - recordedToolCalls = []; - recordedToolResults = []; - recordedStepText = ""; + } + await (onStepFinish == null ? void 0 : onStepFinish(currentStepResult)) + recordedSteps.push(currentStepResult) + recordedToolCalls = [] + recordedToolResults = [] + recordedStepText = "" if (nextStepType !== "done") { - stepType = nextStepType; + stepType = nextStepType } if (nextStepType !== "continue") { - recordedResponse.messages.push(...stepMessages); - recordedContinuationText = ""; + recordedResponse.messages.push(...stepMessages) + recordedContinuationText = "" } } if (part.type === "finish") { - recordedResponse.id = part.response.id; - recordedResponse.timestamp = part.response.timestamp; - recordedResponse.modelId = part.response.modelId; - recordedResponse.headers = part.response.headers; - recordedUsage = part.usage; - recordedFinishReason = part.finishReason; + recordedResponse.id = part.response.id + recordedResponse.timestamp = part.response.timestamp + recordedResponse.modelId = part.response.modelId + recordedResponse.headers = part.response.headers + recordedUsage = part.usage + recordedFinishReason = part.finishReason } }, async flush(controller) { - var _a15; + var _a15 try { if (recordedSteps.length === 0) { - return; + return } - const lastStep = recordedSteps[recordedSteps.length - 1]; - self.warningsPromise.resolve(lastStep.warnings); - self.requestPromise.resolve(lastStep.request); - self.responsePromise.resolve(lastStep.response); - self.toolCallsPromise.resolve(lastStep.toolCalls); - self.toolResultsPromise.resolve(lastStep.toolResults); + const lastStep = recordedSteps[recordedSteps.length - 1] + self.warningsPromise.resolve(lastStep.warnings) + self.requestPromise.resolve(lastStep.request) + self.responsePromise.resolve(lastStep.response) + self.toolCallsPromise.resolve(lastStep.toolCalls) + self.toolResultsPromise.resolve(lastStep.toolResults) self.providerMetadataPromise.resolve( lastStep.experimental_providerMetadata - ); - const finishReason = recordedFinishReason != null ? recordedFinishReason : "unknown"; + ) + const finishReason = recordedFinishReason != null ? recordedFinishReason : "unknown" const usage = recordedUsage != null ? recordedUsage : { completionTokens: NaN, promptTokens: NaN, totalTokens: NaN - }; - self.finishReasonPromise.resolve(finishReason); - self.usagePromise.resolve(usage); - self.textPromise.resolve(recordedFullText); - self.stepsPromise.resolve(recordedSteps); + } + self.finishReasonPromise.resolve(finishReason) + self.usagePromise.resolve(usage) + self.textPromise.resolve(recordedFullText) + self.stepsPromise.resolve(recordedSteps) await (onFinish == null ? void 0 : onFinish({ finishReason, logprobs: void 0, @@ -4793,7 +4798,7 @@ var DefaultStreamTextResult = class { warnings: lastStep.warnings, experimental_providerMetadata: lastStep.experimental_providerMetadata, steps: recordedSteps - })); + })) rootSpan.setAttributes( selectTelemetryAttributes({ telemetry, @@ -4802,47 +4807,47 @@ var DefaultStreamTextResult = class { "ai.response.text": { output: () => recordedFullText }, "ai.response.toolCalls": { output: () => { - var _a16; - return ((_a16 = lastStep.toolCalls) == null ? void 0 : _a16.length) ? JSON.stringify(lastStep.toolCalls) : void 0; + var _a16 + return ((_a16 = lastStep.toolCalls) == null ? void 0 : _a16.length) ? JSON.stringify(lastStep.toolCalls) : void 0 } }, "ai.usage.promptTokens": usage.promptTokens, "ai.usage.completionTokens": usage.completionTokens } }) - ); + ) } catch (error) { - controller.error(error); + controller.error(error) } finally { - rootSpan.end(); + rootSpan.end() } } - }); - const stitchableStream = createStitchableStream(); - this.addStream = stitchableStream.addStream; - this.closeStream = stitchableStream.close; - let stream = stitchableStream.stream; + }) + const stitchableStream = createStitchableStream() + this.addStream = stitchableStream.addStream + this.closeStream = stitchableStream.close + let stream = stitchableStream.stream for (const transform of transforms) { stream = stream.pipeThrough( transform({ tools, stopStream() { - stitchableStream.terminate(); + stitchableStream.terminate() } }) - ); + ) } - this.baseStream = stream.pipeThrough(createOutputTransformStream(output)).pipeThrough(eventProcessor); + this.baseStream = stream.pipeThrough(createOutputTransformStream(output)).pipeThrough(eventProcessor) const { maxRetries, retry } = prepareRetries({ maxRetries: maxRetriesArg - }); - const tracer = getTracer(telemetry); + }) + const tracer = getTracer(telemetry) const baseTelemetryAttributes = getBaseTelemetryAttributes({ model, telemetry, headers, settings: { ...settings, maxRetries } - }); + }) const initialPrompt = standardizePrompt({ prompt: { system: (_a14 = output == null ? void 0 : output.injectIntoSystemPrompt({ system, model })) != null ? _a14 : system, @@ -4850,8 +4855,8 @@ var DefaultStreamTextResult = class { messages }, tools - }); - const self = this; + }) + const self = this recordSpan({ name: "ai.streamText", attributes: selectTelemetryAttributes({ @@ -4869,7 +4874,7 @@ var DefaultStreamTextResult = class { tracer, endWhenDone: false, fn: async (rootSpanArg) => { - rootSpan = rootSpanArg; + rootSpan = rootSpanArg async function streamStep({ currentStep, responseMessages, @@ -4879,11 +4884,11 @@ var DefaultStreamTextResult = class { hasLeadingWhitespace, messageId }) { - const promptFormat = responseMessages.length === 0 ? initialPrompt.type : "messages"; + const promptFormat = responseMessages.length === 0 ? initialPrompt.type : "messages" const stepInputMessages = [ ...initialPrompt.messages, ...responseMessages - ]; + ] const promptMessages = await convertToLanguageModelPrompt({ prompt: { type: promptFormat, @@ -4892,11 +4897,11 @@ var DefaultStreamTextResult = class { }, modelSupportsImageUrls: model.supportsImageUrls, modelSupportsUrl: model.supportsUrl - }); + }) const mode = { type: "regular", ...prepareToolsAndToolChoice({ tools, toolChoice, activeTools }) - }; + } const { result: { stream: stream2, warnings, rawResponse, request }, doStreamSpan, @@ -4921,8 +4926,8 @@ var DefaultStreamTextResult = class { "ai.prompt.tools": { // convert the language model level tools: input: () => { - var _a15; - return (_a15 = mode.tools) == null ? void 0 : _a15.map((tool2) => JSON.stringify(tool2)); + var _a15 + return (_a15 = mode.tools) == null ? void 0 : _a15.map((tool2) => JSON.stringify(tool2)) } }, "ai.prompt.toolChoice": { @@ -4958,7 +4963,7 @@ var DefaultStreamTextResult = class { }) }) }) - ); + ) const transformedStream = runToolsTransformation({ tools, generatorStream: stream2, @@ -4969,151 +4974,151 @@ var DefaultStreamTextResult = class { messages: stepInputMessages, repairToolCall, abortSignal - }); - const stepRequest = request != null ? request : {}; - const stepToolCalls = []; - const stepToolResults = []; - let stepFinishReason = "unknown"; + }) + const stepRequest = request != null ? request : {} + const stepToolCalls = [] + const stepToolResults = [] + let stepFinishReason = "unknown" let stepUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 - }; - let stepProviderMetadata; - let stepFirstChunk = true; - let stepText = ""; - let fullStepText = stepType2 === "continue" ? previousStepText : ""; - let stepLogProbs; + } + let stepProviderMetadata + let stepFirstChunk = true + let stepText = "" + let fullStepText = stepType2 === "continue" ? previousStepText : "" + let stepLogProbs let stepResponse = { id: generateId3(), timestamp: currentDate(), modelId: model.modelId - }; - let chunkBuffer = ""; - let chunkTextPublished = false; - let inWhitespacePrefix = true; - let hasWhitespaceSuffix = false; + } + let chunkBuffer = "" + let chunkTextPublished = false + let inWhitespacePrefix = true + let hasWhitespaceSuffix = false async function publishTextChunk({ controller, chunk }) { - controller.enqueue(chunk); - stepText += chunk.textDelta; - fullStepText += chunk.textDelta; - chunkTextPublished = true; - hasWhitespaceSuffix = chunk.textDelta.trimEnd() !== chunk.textDelta; + controller.enqueue(chunk) + stepText += chunk.textDelta + fullStepText += chunk.textDelta + chunkTextPublished = true + hasWhitespaceSuffix = chunk.textDelta.trimEnd() !== chunk.textDelta } self.addStream( transformedStream.pipeThrough( new TransformStream({ async transform(chunk, controller) { - var _a15, _b, _c; + var _a15, _b, _c if (stepFirstChunk) { - const msToFirstChunk = now2() - startTimestampMs; - stepFirstChunk = false; + const msToFirstChunk = now2() - startTimestampMs + stepFirstChunk = false doStreamSpan.addEvent("ai.stream.firstChunk", { "ai.response.msToFirstChunk": msToFirstChunk - }); + }) doStreamSpan.setAttributes({ "ai.response.msToFirstChunk": msToFirstChunk - }); + }) controller.enqueue({ type: "step-start", messageId, request: stepRequest, warnings: warnings != null ? warnings : [] - }); + }) } if (chunk.type === "text-delta" && chunk.textDelta.length === 0) { - return; + return } - const chunkType = chunk.type; + const chunkType = chunk.type switch (chunkType) { case "text-delta": { if (continueSteps) { - const trimmedChunkText = inWhitespacePrefix && hasLeadingWhitespace ? chunk.textDelta.trimStart() : chunk.textDelta; + const trimmedChunkText = inWhitespacePrefix && hasLeadingWhitespace ? chunk.textDelta.trimStart() : chunk.textDelta if (trimmedChunkText.length === 0) { - break; + break } - inWhitespacePrefix = false; - chunkBuffer += trimmedChunkText; - const split = splitOnLastWhitespace(chunkBuffer); + inWhitespacePrefix = false + chunkBuffer += trimmedChunkText + const split = splitOnLastWhitespace(chunkBuffer) if (split != null) { - chunkBuffer = split.suffix; + chunkBuffer = split.suffix await publishTextChunk({ controller, chunk: { type: "text-delta", textDelta: split.prefix + split.whitespace } - }); + }) } } else { - await publishTextChunk({ controller, chunk }); + await publishTextChunk({ controller, chunk }) } - break; + break } case "tool-call": { - controller.enqueue(chunk); - stepToolCalls.push(chunk); - break; + controller.enqueue(chunk) + stepToolCalls.push(chunk) + break } case "tool-result": { - controller.enqueue(chunk); - stepToolResults.push(chunk); - break; + controller.enqueue(chunk) + stepToolResults.push(chunk) + break } case "response-metadata": { stepResponse = { id: (_a15 = chunk.id) != null ? _a15 : stepResponse.id, timestamp: (_b = chunk.timestamp) != null ? _b : stepResponse.timestamp, modelId: (_c = chunk.modelId) != null ? _c : stepResponse.modelId - }; - break; + } + break } case "finish": { - stepUsage = chunk.usage; - stepFinishReason = chunk.finishReason; - stepProviderMetadata = chunk.experimental_providerMetadata; - stepLogProbs = chunk.logprobs; - const msToFinish = now2() - startTimestampMs; - doStreamSpan.addEvent("ai.stream.finish"); + stepUsage = chunk.usage + stepFinishReason = chunk.finishReason + stepProviderMetadata = chunk.experimental_providerMetadata + stepLogProbs = chunk.logprobs + const msToFinish = now2() - startTimestampMs + doStreamSpan.addEvent("ai.stream.finish") doStreamSpan.setAttributes({ "ai.response.msToFinish": msToFinish, "ai.response.avgCompletionTokensPerSecond": 1e3 * stepUsage.completionTokens / msToFinish - }); - break; + }) + break } case "tool-call-streaming-start": case "tool-call-delta": { - controller.enqueue(chunk); - break; + controller.enqueue(chunk) + break } case "error": { - controller.enqueue(chunk); - stepFinishReason = "error"; - break; + controller.enqueue(chunk) + stepFinishReason = "error" + break } default: { - const exhaustiveCheck = chunkType; - throw new Error(`Unknown chunk type: ${exhaustiveCheck}`); + const exhaustiveCheck = chunkType + throw new Error(`Unknown chunk type: ${exhaustiveCheck}`) } } }, // invoke onFinish callback and resolve toolResults promise when the stream is about to close: async flush(controller) { - const stepToolCallsJson = stepToolCalls.length > 0 ? JSON.stringify(stepToolCalls) : void 0; - let nextStepType = "done"; + const stepToolCallsJson = stepToolCalls.length > 0 ? JSON.stringify(stepToolCalls) : void 0 + let nextStepType = "done" if (currentStep + 1 < maxSteps) { if (continueSteps && stepFinishReason === "length" && // only use continue when there are no tool calls: stepToolCalls.length === 0) { - nextStepType = "continue"; + nextStepType = "continue" } else if ( // there are tool calls: stepToolCalls.length > 0 && // all current tool calls have results: stepToolResults.length === stepToolCalls.length ) { - nextStepType = "tool-result"; + nextStepType = "tool-result" } } if (continueSteps && chunkBuffer.length > 0 && (nextStepType !== "continue" || // when the next step is a regular step, publish the buffer @@ -5124,8 +5129,8 @@ var DefaultStreamTextResult = class { type: "text-delta", textDelta: chunkBuffer } - }); - chunkBuffer = ""; + }) + chunkBuffer = "" } try { doStreamSpan.setAttributes( @@ -5150,10 +5155,10 @@ var DefaultStreamTextResult = class { "gen_ai.usage.output_tokens": stepUsage.completionTokens } }) - ); + ) } catch (error) { } finally { - doStreamSpan.end(); + doStreamSpan.end() } controller.enqueue({ type: "step-finish", @@ -5169,8 +5174,8 @@ var DefaultStreamTextResult = class { warnings, isContinued: nextStepType === "continue", messageId - }); - const combinedUsage = addLanguageModelUsage(usage, stepUsage); + }) + const combinedUsage = addLanguageModelUsage(usage, stepUsage) if (nextStepType === "done") { controller.enqueue({ type: "finish", @@ -5182,18 +5187,18 @@ var DefaultStreamTextResult = class { ...stepResponse, headers: rawResponse == null ? void 0 : rawResponse.headers } - }); - self.closeStream(); + }) + self.closeStream() } else { if (stepType2 === "continue") { - const lastMessage = responseMessages[responseMessages.length - 1]; + const lastMessage = responseMessages[responseMessages.length - 1] if (typeof lastMessage.content === "string") { - lastMessage.content += stepText; + lastMessage.content += stepText } else { lastMessage.content.push({ text: stepText, type: "text" - }); + }) } } else { responseMessages.push( @@ -5205,7 +5210,7 @@ var DefaultStreamTextResult = class { messageId, generateMessageId }) - ); + ) } await streamStep({ currentStep: currentStep + 1, @@ -5218,12 +5223,12 @@ var DefaultStreamTextResult = class { // keep the same id when continuing a step: nextStepType === "continue" ? messageId : generateMessageId() ) - }); + }) } } }) ) - ); + ) } await streamStep({ currentStep: 0, @@ -5237,49 +5242,49 @@ var DefaultStreamTextResult = class { stepType: "initial", hasLeadingWhitespace: false, messageId: generateMessageId() - }); + }) } }).catch((error) => { self.addStream( new ReadableStream({ start(controller) { - controller.enqueue({ type: "error", error }); - controller.close(); + controller.enqueue({ type: "error", error }) + controller.close() } }) - ); - self.closeStream(); - }); + ) + self.closeStream() + }) } get warnings() { - return this.warningsPromise.value; + return this.warningsPromise.value } get usage() { - return this.usagePromise.value; + return this.usagePromise.value } get finishReason() { - return this.finishReasonPromise.value; + return this.finishReasonPromise.value } get experimental_providerMetadata() { - return this.providerMetadataPromise.value; + return this.providerMetadataPromise.value } get text() { - return this.textPromise.value; + return this.textPromise.value } get toolCalls() { - return this.toolCallsPromise.value; + return this.toolCallsPromise.value } get toolResults() { - return this.toolResultsPromise.value; + return this.toolResultsPromise.value } get request() { - return this.requestPromise.value; + return this.requestPromise.value } get response() { - return this.responsePromise.value; + return this.responsePromise.value } get steps() { - return this.stepsPromise.value; + return this.stepsPromise.value } /** Split out a new stream from the original stream. @@ -5290,9 +5295,9 @@ var DefaultStreamTextResult = class { However, the LLM results are expected to be small enough to not cause issues. */ teeStream() { - const [stream1, stream2] = this.baseStream.tee(); - this.baseStream = stream2; - return stream1; + const [stream1, stream2] = this.baseStream.tee() + this.baseStream = stream2 + return stream1 } get textStream() { return createAsyncIterableStream( @@ -5300,63 +5305,63 @@ var DefaultStreamTextResult = class { new TransformStream({ transform({ part }, controller) { if (part.type === "text-delta") { - controller.enqueue(part.textDelta); + controller.enqueue(part.textDelta) } else if (part.type === "error") { - controller.error(part.error); + controller.error(part.error) } } }) ) - ); + ) } get fullStream() { return createAsyncIterableStream( this.teeStream().pipeThrough( new TransformStream({ transform({ part }, controller) { - controller.enqueue(part); + controller.enqueue(part) } }) ) - ); + ) } get experimental_partialOutputStream() { if (this.output == null) { - throw new NoOutputSpecifiedError(); + throw new NoOutputSpecifiedError() } return createAsyncIterableStream( this.teeStream().pipeThrough( new TransformStream({ transform({ partialOutput }, controller) { if (partialOutput != null) { - controller.enqueue(partialOutput); + controller.enqueue(partialOutput) } } }) ) - ); + ) } toDataStreamInternal({ getErrorMessage: getErrorMessage5 = () => "An error occurred.", // mask error messages for safety by default sendUsage = true } = {}) { - let aggregatedResponse = ""; + let aggregatedResponse = "" const callbackTransformer = new TransformStream({ async transform(chunk, controller) { - controller.enqueue(chunk); + controller.enqueue(chunk) if (chunk.type === "text-delta") { - aggregatedResponse += chunk.textDelta; + aggregatedResponse += chunk.textDelta } } - }); + }) const streamPartsTransformer = new TransformStream({ transform: async (chunk, controller) => { - const chunkType = chunk.type; + const chunkType = chunk.type switch (chunkType) { case "text-delta": { - controller.enqueue((0, import_ui_utils8.formatDataStreamPart)("text", chunk.textDelta)); - break; + controller.enqueue((0, import_ui_utils8.formatDataStreamPart)("text", chunk.textDelta)) + break } case "tool-call-streaming-start": { controller.enqueue( @@ -5364,8 +5369,8 @@ var DefaultStreamTextResult = class { toolCallId: chunk.toolCallId, toolName: chunk.toolName }) - ); - break; + ) + break } case "tool-call-delta": { controller.enqueue( @@ -5373,8 +5378,8 @@ var DefaultStreamTextResult = class { toolCallId: chunk.toolCallId, argsTextDelta: chunk.argsTextDelta }) - ); - break; + ) + break } case "tool-call": { controller.enqueue( @@ -5383,8 +5388,8 @@ var DefaultStreamTextResult = class { toolName: chunk.toolName, args: chunk.args }) - ); - break; + ) + break } case "tool-result": { controller.enqueue( @@ -5392,22 +5397,22 @@ var DefaultStreamTextResult = class { toolCallId: chunk.toolCallId, result: chunk.result }) - ); - break; + ) + break } case "error": { controller.enqueue( (0, import_ui_utils8.formatDataStreamPart)("error", getErrorMessage5(chunk.error)) - ); - break; + ) + break } case "step-start": { controller.enqueue( (0, import_ui_utils8.formatDataStreamPart)("start_step", { messageId: chunk.messageId }) - ); - break; + ) + break } case "step-finish": { controller.enqueue( @@ -5419,8 +5424,8 @@ var DefaultStreamTextResult = class { } : void 0, isContinued: chunk.isContinued }) - ); - break; + ) + break } case "finish": { controller.enqueue( @@ -5431,17 +5436,17 @@ var DefaultStreamTextResult = class { completionTokens: chunk.usage.completionTokens } : void 0 }) - ); - break; + ) + break } default: { - const exhaustiveCheck = chunkType; - throw new Error(`Unknown chunk type: ${exhaustiveCheck}`); + const exhaustiveCheck = chunkType + throw new Error(`Unknown chunk type: ${exhaustiveCheck}`) } } } - }); - return this.fullStream.pipeThrough(callbackTransformer).pipeThrough(streamPartsTransformer); + }) + return this.fullStream.pipeThrough(callbackTransformer).pipeThrough(streamPartsTransformer) } pipeDataStreamToResponse(response, { status, @@ -5460,7 +5465,7 @@ var DefaultStreamTextResult = class { dataStreamVersion: "v1" }), stream: this.toDataStream({ data, getErrorMessage: getErrorMessage5, sendUsage }) - }); + }) } pipeTextStreamToResponse(response, init) { writeToServerResponse({ @@ -5471,22 +5476,22 @@ var DefaultStreamTextResult = class { contentType: "text/plain; charset=utf-8" }), stream: this.textStream.pipeThrough(new TextEncoderStream()) - }); + }) } // TODO breaking change 5.0: remove pipeThrough(new TextEncoderStream()) toDataStream(options) { const stream = this.toDataStreamInternal({ getErrorMessage: options == null ? void 0 : options.getErrorMessage, sendUsage: options == null ? void 0 : options.sendUsage - }).pipeThrough(new TextEncoderStream()); - return (options == null ? void 0 : options.data) ? mergeStreams(options == null ? void 0 : options.data.stream, stream) : stream; + }).pipeThrough(new TextEncoderStream()) + return (options == null ? void 0 : options.data) ? mergeStreams(options == null ? void 0 : options.data.stream, stream) : stream } mergeIntoDataStream(writer) { writer.merge( this.toDataStreamInternal({ getErrorMessage: writer.onError }) - ); + ) } toDataStreamResponse({ headers, @@ -5506,18 +5511,18 @@ var DefaultStreamTextResult = class { dataStreamVersion: "v1" }) } - ); + ) } toTextStreamResponse(init) { - var _a14; + var _a14 return new Response(this.textStream.pipeThrough(new TextEncoderStream()), { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, headers: prepareResponseHeaders(init == null ? void 0 : init.headers, { contentType: "text/plain; charset=utf-8" }) - }); + }) } -}; +} // core/middleware/wrap-language-model.ts var experimental_wrapLanguageModel = ({ @@ -5530,7 +5535,7 @@ var experimental_wrapLanguageModel = ({ params, type }) { - return transformParams ? await transformParams({ params, type }) : params; + return transformParams ? await transformParams({ params, type }) : params } return { specificationVersion: "v1", @@ -5541,27 +5546,27 @@ var experimental_wrapLanguageModel = ({ supportsUrl: model.supportsUrl, supportsStructuredOutputs: model.supportsStructuredOutputs, async doGenerate(params) { - const transformedParams = await doTransform({ params, type: "generate" }); - const doGenerate = async () => model.doGenerate(transformedParams); - return wrapGenerate ? wrapGenerate({ doGenerate, params: transformedParams, model }) : doGenerate(); + const transformedParams = await doTransform({ params, type: "generate" }) + const doGenerate = async () => model.doGenerate(transformedParams) + return wrapGenerate ? wrapGenerate({ doGenerate, params: transformedParams, model }) : doGenerate() }, async doStream(params) { - const transformedParams = await doTransform({ params, type: "stream" }); - const doStream = async () => model.doStream(transformedParams); - return wrapStream ? wrapStream({ doStream, params: transformedParams, model }) : doStream(); + const transformedParams = await doTransform({ params, type: "stream" }) + const doStream = async () => model.doStream(transformedParams) + return wrapStream ? wrapStream({ doStream, params: transformedParams, model }) : doStream() } - }; -}; + } +} // core/prompt/append-response-messages.ts function appendResponseMessages({ messages, responseMessages }) { - var _a14; - const clonedMessages = structuredClone(messages); + var _a14 + const clonedMessages = structuredClone(messages) for (const message of responseMessages) { - const role = message.role; + const role = message.role switch (role) { case "assistant": { clonedMessages.push({ @@ -5576,41 +5581,41 @@ function appendResponseMessages({ state: "call", ...call })) - }); - break; + }) + break } case "tool": { const previousMessage = clonedMessages[clonedMessages.length - 1]; - (_a14 = previousMessage.toolInvocations) != null ? _a14 : previousMessage.toolInvocations = []; + (_a14 = previousMessage.toolInvocations) != null ? _a14 : previousMessage.toolInvocations = [] if (previousMessage.role !== "assistant") { throw new Error( `Tool result must follow an assistant message: ${previousMessage.role}` - ); + ) } for (const part of message.content) { const toolCall = previousMessage.toolInvocations.find( (call) => call.toolCallId === part.toolCallId - ); + ) if (!toolCall) { - throw new Error("Tool call not found in previous message"); + throw new Error("Tool call not found in previous message") } - toolCall.state = "result"; - const toolResult = toolCall; - toolResult.result = part.result; + toolCall.state = "result" + const toolResult = toolCall + toolResult.result = part.result } - break; + break } default: { - const _exhaustiveCheck = role; - throw new Error(`Unsupported message role: ${_exhaustiveCheck}`); + const _exhaustiveCheck = role + throw new Error(`Unsupported message role: ${_exhaustiveCheck}`) } } } - return clonedMessages; + return clonedMessages } // core/registry/custom-provider.ts -var import_provider18 = require("@ai-sdk/provider"); +var import_provider18 = require("@ai-sdk/provider") function experimental_customProvider({ languageModels, textEmbeddingModels, @@ -5619,31 +5624,31 @@ function experimental_customProvider({ return { languageModel(modelId) { if (languageModels != null && modelId in languageModels) { - return languageModels[modelId]; + return languageModels[modelId] } if (fallbackProvider) { - return fallbackProvider.languageModel(modelId); + return fallbackProvider.languageModel(modelId) } - throw new import_provider18.NoSuchModelError({ modelId, modelType: "languageModel" }); + throw new import_provider18.NoSuchModelError({ modelId, modelType: "languageModel" }) }, textEmbeddingModel(modelId) { if (textEmbeddingModels != null && modelId in textEmbeddingModels) { - return textEmbeddingModels[modelId]; + return textEmbeddingModels[modelId] } if (fallbackProvider) { - return fallbackProvider.textEmbeddingModel(modelId); + return fallbackProvider.textEmbeddingModel(modelId) } - throw new import_provider18.NoSuchModelError({ modelId, modelType: "textEmbeddingModel" }); + throw new import_provider18.NoSuchModelError({ modelId, modelType: "textEmbeddingModel" }) } - }; + } } // core/registry/no-such-provider-error.ts -var import_provider19 = require("@ai-sdk/provider"); -var name13 = "AI_NoSuchProviderError"; -var marker13 = `vercel.ai.error.${name13}`; -var symbol13 = Symbol.for(marker13); -var _a13; +var import_provider19 = require("@ai-sdk/provider") +var name13 = "AI_NoSuchProviderError" +var marker13 = `vercel.ai.error.${name13}` +var symbol13 = Symbol.for(marker13) +var _a13 var NoSuchProviderError = class extends import_provider19.NoSuchModelError { constructor({ modelId, @@ -5652,123 +5657,123 @@ var NoSuchProviderError = class extends import_provider19.NoSuchModelError { availableProviders, message = `No such provider: ${providerId} (available providers: ${availableProviders.join()})` }) { - super({ errorName: name13, modelId, modelType, message }); - this[_a13] = true; - this.providerId = providerId; - this.availableProviders = availableProviders; + super({ errorName: name13, modelId, modelType, message }) + this[_a13] = true + this.providerId = providerId + this.availableProviders = availableProviders } static isInstance(error) { - return import_provider19.AISDKError.hasMarker(error, marker13); + return import_provider19.AISDKError.hasMarker(error, marker13) } -}; -_a13 = symbol13; +} +_a13 = symbol13 // core/registry/provider-registry.ts -var import_provider20 = require("@ai-sdk/provider"); +var import_provider20 = require("@ai-sdk/provider") function experimental_createProviderRegistry(providers) { - const registry = new DefaultProviderRegistry(); + const registry = new DefaultProviderRegistry() for (const [id, provider] of Object.entries(providers)) { - registry.registerProvider({ id, provider }); + registry.registerProvider({ id, provider }) } - return registry; + return registry } var DefaultProviderRegistry = class { constructor() { - this.providers = {}; + this.providers = {} } registerProvider({ id, provider }) { - this.providers[id] = provider; + this.providers[id] = provider } getProvider(id) { - const provider = this.providers[id]; + const provider = this.providers[id] if (provider == null) { throw new NoSuchProviderError({ modelId: id, modelType: "languageModel", providerId: id, availableProviders: Object.keys(this.providers) - }); + }) } - return provider; + return provider } splitId(id, modelType) { - const index = id.indexOf(":"); + const index = id.indexOf(":") if (index === -1) { throw new import_provider20.NoSuchModelError({ modelId: id, modelType, message: `Invalid ${modelType} id for registry: ${id} (must be in the format "providerId:modelId")` - }); + }) } - return [id.slice(0, index), id.slice(index + 1)]; + return [id.slice(0, index), id.slice(index + 1)] } languageModel(id) { - var _a14, _b; - const [providerId, modelId] = this.splitId(id, "languageModel"); - const model = (_b = (_a14 = this.getProvider(providerId)).languageModel) == null ? void 0 : _b.call(_a14, modelId); + var _a14, _b + const [providerId, modelId] = this.splitId(id, "languageModel") + const model = (_b = (_a14 = this.getProvider(providerId)).languageModel) == null ? void 0 : _b.call(_a14, modelId) if (model == null) { - throw new import_provider20.NoSuchModelError({ modelId: id, modelType: "languageModel" }); + throw new import_provider20.NoSuchModelError({ modelId: id, modelType: "languageModel" }) } - return model; + return model } textEmbeddingModel(id) { - var _a14; - const [providerId, modelId] = this.splitId(id, "textEmbeddingModel"); - const provider = this.getProvider(providerId); - const model = (_a14 = provider.textEmbeddingModel) == null ? void 0 : _a14.call(provider, modelId); + var _a14 + const [providerId, modelId] = this.splitId(id, "textEmbeddingModel") + const provider = this.getProvider(providerId) + const model = (_a14 = provider.textEmbeddingModel) == null ? void 0 : _a14.call(provider, modelId) if (model == null) { throw new import_provider20.NoSuchModelError({ modelId: id, modelType: "textEmbeddingModel" - }); + }) } - return model; + return model } /** * @deprecated Use `textEmbeddingModel` instead. */ textEmbedding(id) { - return this.textEmbeddingModel(id); + return this.textEmbeddingModel(id) } -}; +} // core/tool/tool.ts function tool(tool2) { - return tool2; + return tool2 } // core/util/cosine-similarity.ts function cosineSimilarity(vector1, vector2, options = { throwErrorForEmptyVectors: false }) { - const { throwErrorForEmptyVectors } = options; + const { throwErrorForEmptyVectors } = options if (vector1.length !== vector2.length) { throw new Error( `Vectors must have the same length (vector1: ${vector1.length} elements, vector2: ${vector2.length} elements)` - ); + ) } if (throwErrorForEmptyVectors && vector1.length === 0) { throw new InvalidArgumentError({ parameter: "vector1", value: vector1, message: "Vectors cannot be empty" - }); + }) } - const magnitude1 = magnitude(vector1); - const magnitude2 = magnitude(vector2); + const magnitude1 = magnitude(vector1) + const magnitude2 = magnitude(vector2) if (magnitude1 === 0 || magnitude2 === 0) { - return 0; + return 0 } - return dotProduct(vector1, vector2) / (magnitude1 * magnitude2); + return dotProduct(vector1, vector2) / (magnitude1 * magnitude2) } function dotProduct(vector1, vector2) { return vector1.reduce( (accumulator, value, index) => accumulator + value * vector2[index], 0 - ); + ) } function magnitude(vector) { - return Math.sqrt(dotProduct(vector, vector)); + return Math.sqrt(dotProduct(vector, vector)) } // core/util/simulate-readable-stream.ts @@ -5778,50 +5783,50 @@ function simulateReadableStream({ chunkDelayInMs = 0, _internal }) { - var _a14; - const delay2 = (_a14 = _internal == null ? void 0 : _internal.delay) != null ? _a14 : delay; - let index = 0; + var _a14 + const delay2 = (_a14 = _internal == null ? void 0 : _internal.delay) != null ? _a14 : delay + let index = 0 return new ReadableStream({ async pull(controller) { if (index < chunks.length) { - await delay2(index === 0 ? initialDelayInMs : chunkDelayInMs); - controller.enqueue(chunks[index++]); + await delay2(index === 0 ? initialDelayInMs : chunkDelayInMs) + controller.enqueue(chunks[index++]) } else { - controller.close(); + controller.close() } } - }); + }) } // streams/assistant-response.ts -var import_ui_utils10 = require("@ai-sdk/ui-utils"); +var import_ui_utils10 = require("@ai-sdk/ui-utils") function AssistantResponse({ threadId, messageId }, process2) { const stream = new ReadableStream({ async start(controller) { - var _a14; - const textEncoder = new TextEncoder(); + var _a14 + const textEncoder = new TextEncoder() const sendMessage = (message) => { controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("assistant_message", message) ) - ); - }; + ) + } const sendDataMessage = (message) => { controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("data_message", message) ) - ); - }; + ) + } const sendError = (errorMessage) => { controller.enqueue( textEncoder.encode((0, import_ui_utils10.formatAssistantStreamPart)("error", errorMessage)) - ); - }; + ) + } const forwardStream = async (stream2) => { - var _a15, _b; - let result = void 0; + var _a15, _b + let result = void 0 for await (const value of stream2) { switch (value.event) { case "thread.message.created": { @@ -5833,29 +5838,29 @@ function AssistantResponse({ threadId, messageId }, process2) { content: [{ type: "text", text: { value: "" } }] }) ) - ); - break; + ) + break } case "thread.message.delta": { - const content = (_a15 = value.data.delta.content) == null ? void 0 : _a15[0]; + const content = (_a15 = value.data.delta.content) == null ? void 0 : _a15[0] if ((content == null ? void 0 : content.type) === "text" && ((_b = content.text) == null ? void 0 : _b.value) != null) { controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("text", content.text.value) ) - ); + ) } - break; + break } case "thread.run.completed": case "thread.run.requires_action": { - result = value.data; - break; + result = value.data + break } } } - return result; - }; + return result + } controller.enqueue( textEncoder.encode( (0, import_ui_utils10.formatAssistantStreamPart)("assistant_control_data", { @@ -5863,68 +5868,68 @@ function AssistantResponse({ threadId, messageId }, process2) { messageId }) ) - ); + ) try { await process2({ sendMessage, sendDataMessage, forwardStream - }); + }) } catch (error) { - sendError((_a14 = error.message) != null ? _a14 : `${error}`); + sendError((_a14 = error.message) != null ? _a14 : `${error}`) } finally { - controller.close(); + controller.close() } }, pull(controller) { }, cancel() { } - }); + }) return new Response(stream, { status: 200, headers: { "Content-Type": "text/plain; charset=utf-8" } - }); + }) } // streams/langchain-adapter.ts -var langchain_adapter_exports = {}; +var langchain_adapter_exports = {} __export(langchain_adapter_exports, { mergeIntoDataStream: () => mergeIntoDataStream, toDataStream: () => toDataStream, toDataStreamResponse: () => toDataStreamResponse -}); -var import_ui_utils11 = require("@ai-sdk/ui-utils"); +}) +var import_ui_utils11 = require("@ai-sdk/ui-utils") // streams/stream-callbacks.ts function createCallbacksTransformer(callbacks = {}) { - const textEncoder = new TextEncoder(); - let aggregatedResponse = ""; + const textEncoder = new TextEncoder() + let aggregatedResponse = "" return new TransformStream({ async start() { if (callbacks.onStart) - await callbacks.onStart(); + await callbacks.onStart() }, async transform(message, controller) { - controller.enqueue(textEncoder.encode(message)); - aggregatedResponse += message; + controller.enqueue(textEncoder.encode(message)) + aggregatedResponse += message if (callbacks.onToken) - await callbacks.onToken(message); + await callbacks.onToken(message) if (callbacks.onText && typeof message === "string") { - await callbacks.onText(message); + await callbacks.onText(message) } }, async flush() { if (callbacks.onCompletion) { - await callbacks.onCompletion(aggregatedResponse); + await callbacks.onCompletion(aggregatedResponse) } if (callbacks.onFinal) { - await callbacks.onFinal(aggregatedResponse); + await callbacks.onFinal(aggregatedResponse) } } - }); + }) } // streams/langchain-adapter.ts @@ -5932,45 +5937,45 @@ function toDataStreamInternal(stream, callbacks) { return stream.pipeThrough( new TransformStream({ transform: async (value, controller) => { - var _a14; + var _a14 if (typeof value === "string") { - controller.enqueue(value); - return; + controller.enqueue(value) + return } if ("event" in value) { if (value.event === "on_chat_model_stream") { forwardAIMessageChunk( (_a14 = value.data) == null ? void 0 : _a14.chunk, controller - ); + ) } - return; + return } - forwardAIMessageChunk(value, controller); + forwardAIMessageChunk(value, controller) } }) ).pipeThrough(createCallbacksTransformer(callbacks)).pipeThrough(new TextDecoderStream()).pipeThrough( new TransformStream({ transform: async (chunk, controller) => { - controller.enqueue((0, import_ui_utils11.formatDataStreamPart)("text", chunk)); + controller.enqueue((0, import_ui_utils11.formatDataStreamPart)("text", chunk)) } }) - ); + ) } function toDataStream(stream, callbacks) { return toDataStreamInternal(stream, callbacks).pipeThrough( new TextEncoderStream() - ); + ) } function toDataStreamResponse(stream, options) { - var _a14; + var _a14 const dataStream = toDataStreamInternal( stream, options == null ? void 0 : options.callbacks - ).pipeThrough(new TextEncoderStream()); - const data = options == null ? void 0 : options.data; - const init = options == null ? void 0 : options.init; - const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream; + ).pipeThrough(new TextEncoderStream()) + const data = options == null ? void 0 : options.data + const init = options == null ? void 0 : options.init + const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream return new Response(responseStream, { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, statusText: init == null ? void 0 : init.statusText, @@ -5978,61 +5983,61 @@ function toDataStreamResponse(stream, options) { contentType: "text/plain; charset=utf-8", dataStreamVersion: "v1" }) - }); + }) } function mergeIntoDataStream(stream, options) { - options.dataStream.merge(toDataStreamInternal(stream, options.callbacks)); + options.dataStream.merge(toDataStreamInternal(stream, options.callbacks)) } function forwardAIMessageChunk(chunk, controller) { if (typeof chunk.content === "string") { - controller.enqueue(chunk.content); + controller.enqueue(chunk.content) } else { - const content = chunk.content; + const content = chunk.content for (const item of content) { if (item.type === "text") { - controller.enqueue(item.text); + controller.enqueue(item.text) } } } } // streams/llamaindex-adapter.ts -var llamaindex_adapter_exports = {}; +var llamaindex_adapter_exports = {} __export(llamaindex_adapter_exports, { mergeIntoDataStream: () => mergeIntoDataStream2, toDataStream: () => toDataStream2, toDataStreamResponse: () => toDataStreamResponse2 -}); -var import_provider_utils13 = require("@ai-sdk/provider-utils"); -var import_ui_utils12 = require("@ai-sdk/ui-utils"); +}) +var import_provider_utils13 = require("@ai-sdk/provider-utils") +var import_ui_utils12 = require("@ai-sdk/ui-utils") function toDataStreamInternal2(stream, callbacks) { - const trimStart = trimStartOfStream(); + const trimStart = trimStartOfStream() return (0, import_provider_utils13.convertAsyncIteratorToReadableStream)(stream[Symbol.asyncIterator]()).pipeThrough( new TransformStream({ async transform(message, controller) { - controller.enqueue(trimStart(message.delta)); + controller.enqueue(trimStart(message.delta)) } }) ).pipeThrough(createCallbacksTransformer(callbacks)).pipeThrough(new TextDecoderStream()).pipeThrough( new TransformStream({ transform: async (chunk, controller) => { - controller.enqueue((0, import_ui_utils12.formatDataStreamPart)("text", chunk)); + controller.enqueue((0, import_ui_utils12.formatDataStreamPart)("text", chunk)) } }) - ); + ) } function toDataStream2(stream, callbacks) { return toDataStreamInternal2(stream, callbacks).pipeThrough( new TextEncoderStream() - ); + ) } function toDataStreamResponse2(stream, options = {}) { - var _a14; - const { init, data, callbacks } = options; + var _a14 + const { init, data, callbacks } = options const dataStream = toDataStreamInternal2(stream, callbacks).pipeThrough( new TextEncoderStream() - ); - const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream; + ) + const responseStream = data ? mergeStreams(data.stream, dataStream) : dataStream return new Response(responseStream, { status: (_a14 = init == null ? void 0 : init.status) != null ? _a14 : 200, statusText: init == null ? void 0 : init.statusText, @@ -6040,149 +6045,148 @@ function toDataStreamResponse2(stream, options = {}) { contentType: "text/plain; charset=utf-8", dataStreamVersion: "v1" }) - }); + }) } function mergeIntoDataStream2(stream, options) { - options.dataStream.merge(toDataStreamInternal2(stream, options.callbacks)); + options.dataStream.merge(toDataStreamInternal2(stream, options.callbacks)) } function trimStartOfStream() { - let isStreamStart = true; + let isStreamStart = true return (text2) => { if (isStreamStart) { - text2 = text2.trimStart(); + text2 = text2.trimStart() if (text2) - isStreamStart = false; + isStreamStart = false } - return text2; - }; + return text2 + } } // streams/stream-data.ts -var import_ui_utils13 = require("@ai-sdk/ui-utils"); +var import_ui_utils13 = require("@ai-sdk/ui-utils") // util/constants.ts -var HANGING_STREAM_WARNING_TIME_MS = 15 * 1e3; +var HANGING_STREAM_WARNING_TIME_MS = 15 * 1e3 // streams/stream-data.ts var StreamData = class { constructor() { - this.encoder = new TextEncoder(); - this.controller = null; - this.isClosed = false; - this.warningTimeout = null; - const self = this; + this.encoder = new TextEncoder() + this.controller = null + this.isClosed = false + this.warningTimeout = null + const self = this this.stream = new ReadableStream({ start: async (controller) => { - self.controller = controller; + self.controller = controller if (process.env.NODE_ENV === "development") { self.warningTimeout = setTimeout(() => { console.warn( "The data stream is hanging. Did you forget to close it with `data.close()`?" - ); - }, HANGING_STREAM_WARNING_TIME_MS); + ) + }, HANGING_STREAM_WARNING_TIME_MS) } }, pull: (controller) => { }, cancel: (reason) => { - this.isClosed = true; + this.isClosed = true } - }); + }) } async close() { if (this.isClosed) { - throw new Error("Data Stream has already been closed."); + throw new Error("Data Stream has already been closed.") } if (!this.controller) { - throw new Error("Stream controller is not initialized."); + throw new Error("Stream controller is not initialized.") } - this.controller.close(); - this.isClosed = true; + this.controller.close() + this.isClosed = true if (this.warningTimeout) { - clearTimeout(this.warningTimeout); + clearTimeout(this.warningTimeout) } } append(value) { if (this.isClosed) { - throw new Error("Data Stream has already been closed."); + throw new Error("Data Stream has already been closed.") } if (!this.controller) { - throw new Error("Stream controller is not initialized."); + throw new Error("Stream controller is not initialized.") } this.controller.enqueue( this.encoder.encode((0, import_ui_utils13.formatDataStreamPart)("data", [value])) - ); + ) } appendMessageAnnotation(value) { if (this.isClosed) { - throw new Error("Data Stream has already been closed."); + throw new Error("Data Stream has already been closed.") } if (!this.controller) { - throw new Error("Stream controller is not initialized."); + throw new Error("Stream controller is not initialized.") } this.controller.enqueue( this.encoder.encode((0, import_ui_utils13.formatDataStreamPart)("message_annotations", [value])) - ); + ) } -}; +} // Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - AISDKError, - APICallError, +module.exports = { + // AISDKError, + // APICallError, AssistantResponse, DownloadError, - EmptyResponseBodyError, + // EmptyResponseBodyError, InvalidArgumentError, InvalidDataContentError, InvalidMessageRoleError, - InvalidPromptError, - InvalidResponseDataError, + // InvalidPromptError, + // InvalidResponseDataError, InvalidToolArgumentsError, - JSONParseError, - LangChainAdapter, - LlamaIndexAdapter, - LoadAPIKeyError, + // JSONParseError, + // LangChainAdapter, + // LlamaIndexAdapter, + // LoadAPIKeyError, MessageConversionError, - NoContentGeneratedError, + // NoContentGeneratedError, NoObjectGeneratedError, NoOutputSpecifiedError, - NoSuchModelError, + // NoSuchModelError, NoSuchProviderError, NoSuchToolError, - Output, + // Output, RetryError, StreamData, ToolCallRepairError, ToolExecutionError, - TypeValidationError, - UnsupportedFunctionalityError, + // TypeValidationError, + // UnsupportedFunctionalityError, appendResponseMessages, convertToCoreMessages, cosineSimilarity, createDataStream, createDataStreamResponse, - createIdGenerator, + // createIdGenerator, embed, embedMany, experimental_createProviderRegistry, experimental_customProvider, - experimental_generateImage, + // experimental_generateImage, experimental_wrapLanguageModel, - formatAssistantStreamPart, - formatDataStreamPart, - generateId, + // formatAssistantStreamPart, + // formatDataStreamPart, + // generateId, generateObject, generateText, - jsonSchema, - parseAssistantStreamPart, - parseDataStreamPart, + // jsonSchema, + // parseAssistantStreamPart, + // parseDataStreamPart, pipeDataStreamToResponse, - processDataStream, - processTextStream, + // processDataStream, + // processTextStream, simulateReadableStream, smoothStream, streamObject, streamText, tool -}); -//# sourceMappingURL=index.js.map \ No newline at end of file +} diff --git a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js index 122b5ad..9be96ba 100644 --- a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js +++ b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/gigachat.js @@ -607,8 +607,7 @@ function createGigachat(options = {}) { } var gigachat = createGigachat(); // Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { +module.exports = { createGigachat, gigachat -}); -//# sourceMappingURL=index.js.map \ No newline at end of file +} diff --git a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js index 50275f0..da21202 100644 --- a/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js +++ b/server/routers/kfu-m-24-1/eng-it-lean/gigachat/index.js @@ -84,7 +84,7 @@ router.use(async (req, res, next) => { process.env.GIGACHAT_ACCESS_TOKEN = json.access_token; process.env.GIGACHAT_EXPIRES_AT = json.expires_at; console.log(JSON.stringify(response.data)); - } catch { + } catch (error) { console.log(error); } } From df21879c0dd1aea6980883620ef4b758600b8ed3 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Sat, 22 Feb 2025 16:58:58 +0300 Subject: [PATCH 22/41] feat: upload car image --- package-lock.json | 135 +++++++++++++++++- package.json | 1 + .../routers/dry-wash/model/order.car-img.js | 27 ++++ server/routers/dry-wash/order.js | 66 +++++++++ 4 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 server/routers/dry-wash/model/order.car-img.js diff --git a/package-lock.json b/package-lock.json index 9404495..e200158 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "mongoose": "^8.9.2", "mongoose-sequence": "^6.0.1", "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", "pbkdf2-password": "^1.2.1", "rotating-file-stream": "^3.2.5", "socket.io": "^4.8.1", @@ -2084,6 +2085,12 @@ "node": ">= 8" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -2443,9 +2450,19 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, "license": "MIT" }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2708,6 +2725,21 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -2774,6 +2806,12 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -4578,6 +4616,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5841,6 +5885,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -6150,6 +6203,36 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/nanoid": { "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", @@ -6707,6 +6790,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -6834,6 +6923,27 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -7414,6 +7524,14 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7806,6 +7924,12 @@ "node": ">= 0.6" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/uid-safe": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", @@ -8109,6 +8233,15 @@ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 5acc7e8..c7384b9 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "mongoose": "^8.9.2", "mongoose-sequence": "^6.0.1", "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", "pbkdf2-password": "^1.2.1", "rotating-file-stream": "^3.2.5", "socket.io": "^4.8.1", diff --git a/server/routers/dry-wash/model/order.car-img.js b/server/routers/dry-wash/model/order.car-img.js new file mode 100644 index 0000000..1cc9793 --- /dev/null +++ b/server/routers/dry-wash/model/order.car-img.js @@ -0,0 +1,27 @@ +const { Schema, model } = require('mongoose') + +const schema = new Schema({ + file: String, + orderId: { + type: Schema.Types.ObjectId, + ref: 'dry-wash-order' + }, + created: { + type: Date, + default: () => new Date().toISOString(), + }, +}) + +schema.set('toJSON', { + virtuals: true, + versionKey: false, + transform(_doc, ret) { + delete ret._id + } +}) + +schema.virtual('id').get(function () { + return this._id.toHexString() +}) + +exports.OrderCarImgModel = model('dry-wash-order-car-image', schema) \ No newline at end of file diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index d258fa0..30dbd70 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -1,7 +1,9 @@ const mongoose = require("mongoose") const router = require('express').Router() +const multer = require('multer') const { MasterModel } = require('./model/master') const { OrderModel } = require('./model/order') +const { OrderCarImgModel } = require('./model/order.car-img') const { orderStatus } = require('./model/const') const isValidPhoneNumber = (value) => /^(\+)?\d{9,15}/.test(value) @@ -26,6 +28,9 @@ const isValidLocation = (value) => { const isValidOrderStatus = (value) => Object.values(orderStatus).includes(value) const isValidOrderNotes = (value) => value.length < 500 +const allowedMimeTypes = ['image/jpeg', 'image/png'] +const sizeLimitInMegaBytes = 5 + const VALIDATION_MESSAGES = { order: { notFound: 'Order not found' @@ -60,6 +65,13 @@ const VALIDATION_MESSAGES = { carColor: { invalid: 'Invalid car color' }, + carImg: { + required: 'Car image file is required', + invalid: { + type: `Invalid car image file type. Allowed types: ${allowedMimeTypes}`, + size: `Invalid car image file size. Limit is ${sizeLimitInMegaBytes}MB` + } + }, washingBegin: { required: 'Begin time of washing is required', invalid: 'Invalid begin time of washing' @@ -248,4 +260,58 @@ router.delete('/:id', async (req, res, next) => { } }) +const storage = multer.memoryStorage() +const upload = multer({ + storage: storage, + limits: { fileSize: sizeLimitInMegaBytes * 1024 * 1024 }, + fileFilter: (req, file, cb) => { + if (allowedMimeTypes.includes(file.mimetype)) { + cb(null, true) + } else { + cb(new Error(VALIDATION_MESSAGES.carImg.invalid.type), false) + } + } +}) + +const convertFileToBase64 = (file) => { + const base64Image = file.buffer.toString('base64') + return base64Image +} + +router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => { + const { id: orderId } = req.params + if (!mongoose.Types.ObjectId.isValid(orderId)) { + throw new Error(VALIDATION_MESSAGES.orderId.invalid) + } + const order = await OrderModel.findById(orderId) + if (!order) { + throw new Error(VALIDATION_MESSAGES.order.notFound) + } + + if (!req.file) { + throw new Error(VALIDATION_MESSAGES.carImg.required) + } + + const orderCarImg = await OrderCarImgModel.create({ + file: convertFileToBase64(req.file), + orderId, + created: new Date().toISOString(), + }) + + res.status(200).send({ success: true, body: orderCarImg }) +}) + +router.use((err, req, res, next) => { + if (err instanceof multer.MulterError) { + switch (err.message) { + case 'File too large': + return res.status(400).json({ success: false, error: VALIDATION_MESSAGES.carImg.invalid.size }) + default: + return res.status(400).json({ success: false, error: err.message }) + } + } + + throw new Error(err.message) +}) + module.exports = router \ No newline at end of file From 6794b01ac8211ea78afbc97924cf34df30ce8fa9 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Sun, 23 Feb 2025 12:32:53 +0300 Subject: [PATCH 23/41] feat: add fetch image --- server/routers/dry-wash/image.js | 91 ++++++++++++++++++++++++++++++++ server/routers/dry-wash/index.js | 2 + 2 files changed, 93 insertions(+) create mode 100644 server/routers/dry-wash/image.js diff --git a/server/routers/dry-wash/image.js b/server/routers/dry-wash/image.js new file mode 100644 index 0000000..263e342 --- /dev/null +++ b/server/routers/dry-wash/image.js @@ -0,0 +1,91 @@ +const API_URL = "https://gigachat.devices.sberbank.ru/api/v1" +const router = require('express').Router() +const { v4: uuidv4 } = require("uuid"); + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" + +const token = 'MTQwMmNmZjgtZjA5OC00OGMxLWI0OTUtNWU3ZTU4YzMzZjdjOmU5OGFiYmNiLThmMDItNGVmOC1hNjhhLTA4Y2QxYjVmOGRmMA==' + + +const getToken = async (req, res) => { + + const rqUID = uuidv4() + const body = new URLSearchParams({ + scope: "GIGACHAT_API_PERS", + }) + + const response = await fetch("https://ngw.devices.sberbank.ru:9443/api/v2/oauth", { + method: "POST", + headers: { + Authorization: `Basic ${token}`, + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + RqUID: rqUID, + }, + body, + }) + + if (!response.ok) { + const errorData = await response.json(); + console.error(" Ошибка запроса:", errorData); + return res.status(response.status).json(errorData); + } + + return await response.json() +} + +async function analyzeImage(fileId,token) { + const response = await fetch(`${API_URL}/chat/completions`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + model: "GigaChat-Max", + stream: false, + update_interval: 0, + messages: [ + { + role: "system", + content: + "Ты эксперт по оценке чистоты автомобилей. Твоя задача — анализировать фотографии машин и определять степень их чистоты по 10-балльной шкале, где 1 — очень грязная, 10 — полностью чистая. Отвечай только числом от 1 до 10, без пояснений и дополнительных слов.", + }, + { + role: "user", + content: "Что с чистотой машины? Отвечай на основе приложенного документа", + attachments: [fileId], + }, + ], + }), + }); + + const data = await response.json() + console.log(" Результат анализа:", data) + return data +} + +router.post("/upload", async (req, res) => { + + const {access_token} = await getToken(req, res) + + const response = await fetch(`${API_URL}/files`, { + method: "POST", + headers: { + Authorization: `Bearer ${access_token}`, + contentType: "multipart/form-data", + }, + body: req.body, + }) + + const data = await response.json() + + const analysisResponse = await analyzeImage(data.id,access_token) + + res.json({ fileId: data.id, analysis: analysisResponse }) +}) + + + +module.exports = router diff --git a/server/routers/dry-wash/index.js b/server/routers/dry-wash/index.js index 5a89a80..b1dda9f 100644 --- a/server/routers/dry-wash/index.js +++ b/server/routers/dry-wash/index.js @@ -2,11 +2,13 @@ const router = require('express').Router() const armMasterRouter = require('./arm-master') const armOrdersRouter = require('./arm-orders') const orderRouter = require('./order') +const imageRouter = require('./image') router.use('/arm', armMasterRouter) router.use('/arm', armOrdersRouter) router.use('/order', orderRouter) +router.use('/image', imageRouter) module.exports = router From 69c280b266206f999bdcf0154dcc4f9a528b4fb1 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Mon, 3 Mar 2025 18:21:32 +0300 Subject: [PATCH 24/41] evaluate with ai car dirtiness by image --- server/routers/dry-wash/image.js | 91 ----------------- server/routers/dry-wash/index.js | 2 - .../routers/dry-wash/model/order.car-img.js | 4 +- server/routers/dry-wash/order.js | 99 ++++++++++++++++++- 4 files changed, 100 insertions(+), 96 deletions(-) delete mode 100644 server/routers/dry-wash/image.js diff --git a/server/routers/dry-wash/image.js b/server/routers/dry-wash/image.js deleted file mode 100644 index 263e342..0000000 --- a/server/routers/dry-wash/image.js +++ /dev/null @@ -1,91 +0,0 @@ -const API_URL = "https://gigachat.devices.sberbank.ru/api/v1" -const router = require('express').Router() -const { v4: uuidv4 } = require("uuid"); - -process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" - -const token = 'MTQwMmNmZjgtZjA5OC00OGMxLWI0OTUtNWU3ZTU4YzMzZjdjOmU5OGFiYmNiLThmMDItNGVmOC1hNjhhLTA4Y2QxYjVmOGRmMA==' - - -const getToken = async (req, res) => { - - const rqUID = uuidv4() - const body = new URLSearchParams({ - scope: "GIGACHAT_API_PERS", - }) - - const response = await fetch("https://ngw.devices.sberbank.ru:9443/api/v2/oauth", { - method: "POST", - headers: { - Authorization: `Basic ${token}`, - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json", - RqUID: rqUID, - }, - body, - }) - - if (!response.ok) { - const errorData = await response.json(); - console.error(" Ошибка запроса:", errorData); - return res.status(response.status).json(errorData); - } - - return await response.json() -} - -async function analyzeImage(fileId,token) { - const response = await fetch(`${API_URL}/chat/completions`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ - model: "GigaChat-Max", - stream: false, - update_interval: 0, - messages: [ - { - role: "system", - content: - "Ты эксперт по оценке чистоты автомобилей. Твоя задача — анализировать фотографии машин и определять степень их чистоты по 10-балльной шкале, где 1 — очень грязная, 10 — полностью чистая. Отвечай только числом от 1 до 10, без пояснений и дополнительных слов.", - }, - { - role: "user", - content: "Что с чистотой машины? Отвечай на основе приложенного документа", - attachments: [fileId], - }, - ], - }), - }); - - const data = await response.json() - console.log(" Результат анализа:", data) - return data -} - -router.post("/upload", async (req, res) => { - - const {access_token} = await getToken(req, res) - - const response = await fetch(`${API_URL}/files`, { - method: "POST", - headers: { - Authorization: `Bearer ${access_token}`, - contentType: "multipart/form-data", - }, - body: req.body, - }) - - const data = await response.json() - - const analysisResponse = await analyzeImage(data.id,access_token) - - res.json({ fileId: data.id, analysis: analysisResponse }) -}) - - - -module.exports = router diff --git a/server/routers/dry-wash/index.js b/server/routers/dry-wash/index.js index b1dda9f..5a89a80 100644 --- a/server/routers/dry-wash/index.js +++ b/server/routers/dry-wash/index.js @@ -2,13 +2,11 @@ const router = require('express').Router() const armMasterRouter = require('./arm-master') const armOrdersRouter = require('./arm-orders') const orderRouter = require('./order') -const imageRouter = require('./image') router.use('/arm', armMasterRouter) router.use('/arm', armOrdersRouter) router.use('/order', orderRouter) -router.use('/image', imageRouter) module.exports = router diff --git a/server/routers/dry-wash/model/order.car-img.js b/server/routers/dry-wash/model/order.car-img.js index 1cc9793..9b653f0 100644 --- a/server/routers/dry-wash/model/order.car-img.js +++ b/server/routers/dry-wash/model/order.car-img.js @@ -1,7 +1,9 @@ const { Schema, model } = require('mongoose') const schema = new Schema({ - file: String, + image: String, + imageRating: String, + imageDescription: String, orderId: { type: Schema.Types.ObjectId, ref: 'dry-wash-order' diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index 30dbd70..6ee245f 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -273,6 +273,94 @@ const upload = multer({ } }) +const { v4: uuidv4 } = require("uuid") +const axios = require('axios') + +const GIGA_CHAT_ACCESS_TOKEN = 'OGJmMDQ4MzAtNDIwNS00NzRmLTkwOTQtNTdmNGNlYzkxZjc0OjFiZDQ0MDM4LTkyYTktNDdiNy04N2M5LWIzNjAwNjBjZTVjNQ==' +const GIGA_CHAT_OAUTH = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth' +const GIGA_CHAT_API = 'https://gigachat.devices.sberbank.ru/api/v1' + +const getToken = async (req, res) => { + const rqUID = uuidv4() + const body = new URLSearchParams({ + scope: "GIGACHAT_API_PERS", + }) + + const response = await fetch(GIGA_CHAT_OAUTH, { + method: "POST", + headers: { + Authorization: `Basic ${GIGA_CHAT_ACCESS_TOKEN}`, + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", + RqUID: rqUID, + }, + body, + }) + + if (!response.ok) { + const errorData = await response.json() + console.error("Ошибка запроса: ", errorData) + return res.status(response.status).json(errorData) + } + + return await response.json() +} + +const uploadImage = async (file, accessToken) => { + const formData = new FormData() + const blob = new Blob([file.buffer], { type: file.mimetype }) + formData.append('file', blob, file.originalname) + formData.append('purpose', 'general') + + const config = { + maxBodyLength: Infinity, + headers: { + 'Content-Type': 'multipart/form-data', + 'Accept': 'application/json', + 'Authorization': `Bearer ${accessToken}` + } + } + + try { + const response = await axios.post(`${GIGA_CHAT_API}/files`, formData, config) + return response.data.id + } catch (error) { + console.error(error) + } +} + +const analyzeImage = async (fileId, token) => { + const response = await fetch(`${GIGA_CHAT_API}/chat/completions`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify({ + model: "GigaChat-Max", + stream: false, + update_interval: 0, + messages: [ + { + role: "user", + content: + `Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения. Ответ должен содержать: 1. Оценку степени загрязнения в виде числа от 0 до 10, где 0 - без загрязнений, 10 - загрязнена на 100%. 2. Пояснение к оценке в виде абзаца текста с обоснованием выставленной оценки. + Ответ должен быть в виде объекта следующего вида: + { + "value": число от 0 до 10, + "description": "текст с обоснованием оценки" + }`, + attachments: [fileId], + }, + ], + }), + }) + + const data = await response.json() + return JSON.parse(data.choices[0].message.content) +} + const convertFileToBase64 = (file) => { const base64Image = file.buffer.toString('base64') return base64Image @@ -292,8 +380,15 @@ router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => { throw new Error(VALIDATION_MESSAGES.carImg.required) } + const { access_token } = await getToken(req, res) + + const fileId = await uploadImage(req.file, access_token) + const { value, description } = await analyzeImage(fileId, access_token) ?? {} + const orderCarImg = await OrderCarImgModel.create({ - file: convertFileToBase64(req.file), + image: convertFileToBase64(req.file), + imageRating: value, + imageDescription: description, orderId, created: new Date().toISOString(), }) @@ -310,7 +405,7 @@ router.use((err, req, res, next) => { return res.status(400).json({ success: false, error: err.message }) } } - + throw new Error(err.message) }) From c828718498bdfd17f1bd78589265b6f0d2525d26 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Mon, 3 Mar 2025 19:36:56 +0300 Subject: [PATCH 25/41] feat: add today filter --- server/routers/dry-wash/arm-master.js | 207 +++++++++++++------------- 1 file changed, 106 insertions(+), 101 deletions(-) diff --git a/server/routers/dry-wash/arm-master.js b/server/routers/dry-wash/arm-master.js index ce1af19..5403a43 100644 --- a/server/routers/dry-wash/arm-master.js +++ b/server/routers/dry-wash/arm-master.js @@ -1,111 +1,116 @@ -const router = require('express').Router() -const {MasterModel} = require('./model/master') -const mongoose = require("mongoose") -const {OrderModel} = require("./model/order") - +const router = require("express").Router(); +const { MasterModel } = require("./model/master"); +const mongoose = require("mongoose"); +const { OrderModel } = require("./model/order"); router.get("/masters", async (req, res, next) => { - try { - const masters = await MasterModel.find({}); - const orders = await OrderModel.find({}); + try { + const masters = await MasterModel.find({}); - const mastersWithOrders = masters.map((master) => { - const masterOrders = orders.filter((order) => { - return ( - order?.master && order.master.toString() === master._id.toString() - ); - }); + // Создаем объекты для начала и конца текущего дня + const today = new Date(); + today.setHours(0, 0, 0, 0); + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); - const schedule = masterOrders.map((order) => ({ - id: order._id, - startWashTime: order.startWashTime, - endWashTime: order.endWashTime, - })); + const orders = await OrderModel.find({ + startWashTime: { + $gte: today, + $lt: tomorrow, + }, + }); - return { - id: master._id, - name: master.name, - schedule: schedule, - phone: master.phone, - }; - }); - - res.status(200).send({ success: true, body: mastersWithOrders }); - } catch (error) { - next(error); - } -}); - -router.delete('/masters/:id', async (req, res,next) => { - const { id } = req.params; - - if (!mongoose.Types.ObjectId.isValid(id)){ - throw new Error('ID is required') - } - - try { - const master = await MasterModel.findByIdAndDelete(id, { - new: true, - }); - if (!master) { - throw new Error('master not found') - } - res.status(200).send({success: true, body: master}) - } catch (error) { - next(error) - } -}) - - -router.post('/masters', async (req, res,next) => { - - const {name, phone} = req.body - - if (!name || !phone ){ - throw new Error('Enter name and phone') - } - try { - const master = await MasterModel.create({name, phone}) - res.status(200).send({success: true, body: master}) - } catch (error) { - next(error) - } -}) - - -router.patch('/masters/:id', async (req, res, next) => { - const { id } = req.params; - - if (!mongoose.Types.ObjectId.isValid(id)) { - throw new Error('ID is required') - } - - const { name, phone } = req.body; - - if (!name && !phone) { - throw new Error('Enter name and phone') - } - - - try { - const updateData = {}; - if (name) updateData.name = name; - if (phone) updateData.phone = phone; - - const master = await MasterModel.findByIdAndUpdate( - id, - updateData, - { new: true } + const mastersWithOrders = masters.map((master) => { + const masterOrders = orders.filter((order) => { + return ( + order?.master && order.master.toString() === master._id.toString() ); + }); - if (!master) { - throw new Error('master not found') - } + const schedule = masterOrders.map((order) => ({ + id: order._id, + startWashTime: order.startWashTime, + endWashTime: order.endWashTime, + })); - res.status(200).send({ success: true, body: master }); - } catch (error) { - next(error); - } + return { + id: master._id, + name: master.name, + schedule: schedule, + phone: master.phone, + }; + }); + + res.status(200).send({ success: true, body: mastersWithOrders }); + } catch (error) { + next(error); + } }); -module.exports = router +router.delete("/masters/:id", async (req, res, next) => { + const { id } = req.params; + + if (!mongoose.Types.ObjectId.isValid(id)) { + throw new Error("ID is required"); + } + + try { + const master = await MasterModel.findByIdAndDelete(id, { + new: true, + }); + if (!master) { + throw new Error("master not found"); + } + res.status(200).send({ success: true, body: master }); + } catch (error) { + next(error); + } +}); + +router.post("/masters", async (req, res, next) => { + const { name, phone } = req.body; + + if (!name || !phone) { + throw new Error("Enter name and phone"); + } + try { + const master = await MasterModel.create({ name, phone }); + res.status(200).send({ success: true, body: master }); + } catch (error) { + next(error); + } +}); + +router.patch("/masters/:id", async (req, res, next) => { + const { id } = req.params; + + if (!mongoose.Types.ObjectId.isValid(id)) { + throw new Error("ID is required"); + } + + const { name, phone } = req.body; + + if (!name && !phone) { + throw new Error("Enter name and phone"); + } + + try { + const updateData = {}; + if (name) updateData.name = name; + if (phone) updateData.phone = phone; + + const master = await MasterModel.findByIdAndUpdate(id, updateData, { + new: true, + }); + + if (!master) { + throw new Error("master not found"); + } + + res.status(200).send({ success: true, body: master }); + } catch (error) { + next(error); + } +}); + +module.exports = router; From 566bce466305e5e3b90bde35065c8ff8611898f6 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Mon, 3 Mar 2025 19:46:10 +0300 Subject: [PATCH 26/41] feat: delete image --- server/routers/dry-wash/image.js | 91 -------------------------------- server/routers/dry-wash/index.js | 2 - 2 files changed, 93 deletions(-) delete mode 100644 server/routers/dry-wash/image.js diff --git a/server/routers/dry-wash/image.js b/server/routers/dry-wash/image.js deleted file mode 100644 index 263e342..0000000 --- a/server/routers/dry-wash/image.js +++ /dev/null @@ -1,91 +0,0 @@ -const API_URL = "https://gigachat.devices.sberbank.ru/api/v1" -const router = require('express').Router() -const { v4: uuidv4 } = require("uuid"); - -process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" - -const token = 'MTQwMmNmZjgtZjA5OC00OGMxLWI0OTUtNWU3ZTU4YzMzZjdjOmU5OGFiYmNiLThmMDItNGVmOC1hNjhhLTA4Y2QxYjVmOGRmMA==' - - -const getToken = async (req, res) => { - - const rqUID = uuidv4() - const body = new URLSearchParams({ - scope: "GIGACHAT_API_PERS", - }) - - const response = await fetch("https://ngw.devices.sberbank.ru:9443/api/v2/oauth", { - method: "POST", - headers: { - Authorization: `Basic ${token}`, - "Content-Type": "application/x-www-form-urlencoded", - Accept: "application/json", - RqUID: rqUID, - }, - body, - }) - - if (!response.ok) { - const errorData = await response.json(); - console.error(" Ошибка запроса:", errorData); - return res.status(response.status).json(errorData); - } - - return await response.json() -} - -async function analyzeImage(fileId,token) { - const response = await fetch(`${API_URL}/chat/completions`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Accept: "application/json", - Authorization: `Bearer ${token}`, - }, - body: JSON.stringify({ - model: "GigaChat-Max", - stream: false, - update_interval: 0, - messages: [ - { - role: "system", - content: - "Ты эксперт по оценке чистоты автомобилей. Твоя задача — анализировать фотографии машин и определять степень их чистоты по 10-балльной шкале, где 1 — очень грязная, 10 — полностью чистая. Отвечай только числом от 1 до 10, без пояснений и дополнительных слов.", - }, - { - role: "user", - content: "Что с чистотой машины? Отвечай на основе приложенного документа", - attachments: [fileId], - }, - ], - }), - }); - - const data = await response.json() - console.log(" Результат анализа:", data) - return data -} - -router.post("/upload", async (req, res) => { - - const {access_token} = await getToken(req, res) - - const response = await fetch(`${API_URL}/files`, { - method: "POST", - headers: { - Authorization: `Bearer ${access_token}`, - contentType: "multipart/form-data", - }, - body: req.body, - }) - - const data = await response.json() - - const analysisResponse = await analyzeImage(data.id,access_token) - - res.json({ fileId: data.id, analysis: analysisResponse }) -}) - - - -module.exports = router diff --git a/server/routers/dry-wash/index.js b/server/routers/dry-wash/index.js index b1dda9f..5a89a80 100644 --- a/server/routers/dry-wash/index.js +++ b/server/routers/dry-wash/index.js @@ -2,13 +2,11 @@ const router = require('express').Router() const armMasterRouter = require('./arm-master') const armOrdersRouter = require('./arm-orders') const orderRouter = require('./order') -const imageRouter = require('./image') router.use('/arm', armMasterRouter) router.use('/arm', armOrdersRouter) router.use('/order', orderRouter) -router.use('/image', imageRouter) module.exports = router From c0883fc2bc3f2f67f97d316297edf1a401f9c092 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Mon, 3 Mar 2025 19:49:11 +0300 Subject: [PATCH 27/41] add get token, fix prompt --- server/routers/dry-wash/get-token.js | 9 ++++ server/routers/dry-wash/order.js | 72 ++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 server/routers/dry-wash/get-token.js diff --git a/server/routers/dry-wash/get-token.js b/server/routers/dry-wash/get-token.js new file mode 100644 index 0000000..8543dc6 --- /dev/null +++ b/server/routers/dry-wash/get-token.js @@ -0,0 +1,9 @@ +const getGigaToken = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev') + const data = await response.json() + return data.features['dry-wash-bh'].GIGA_TOKEN.value +} + +module.exports = { + getGigaToken +} \ No newline at end of file diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index 6ee245f..8244900 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -5,6 +5,7 @@ const { MasterModel } = require('./model/master') const { OrderModel } = require('./model/order') const { OrderCarImgModel } = require('./model/order.car-img') const { orderStatus } = require('./model/const') +const { getGigaToken } = require('./get-token') const isValidPhoneNumber = (value) => /^(\+)?\d{9,15}/.test(value) const isValidCarNumber = (value) => /^[авекмнорстух][0-9]{3}[авекмнорстух]{2}[0-9]{2,3}$/i.test(value) @@ -276,11 +277,13 @@ const upload = multer({ const { v4: uuidv4 } = require("uuid") const axios = require('axios') -const GIGA_CHAT_ACCESS_TOKEN = 'OGJmMDQ4MzAtNDIwNS00NzRmLTkwOTQtNTdmNGNlYzkxZjc0OjFiZDQ0MDM4LTkyYTktNDdiNy04N2M5LWIzNjAwNjBjZTVjNQ==' +const GIGA_CHAT_ACCESS_TOKEN = 'MTQwMmNmZjgtZjA5OC00OGMxLWI0OTUtNWU3ZTU4YzMzZjdjOmU5OGFiYmNiLThmMDItNGVmOC1hNjhhLTA4Y2QxYjVmOGRmMA==' const GIGA_CHAT_OAUTH = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth' const GIGA_CHAT_API = 'https://gigachat.devices.sberbank.ru/api/v1' const getToken = async (req, res) => { + const gigaToken = await getGigaToken() + const rqUID = uuidv4() const body = new URLSearchParams({ scope: "GIGACHAT_API_PERS", @@ -289,7 +292,7 @@ const getToken = async (req, res) => { const response = await fetch(GIGA_CHAT_OAUTH, { method: "POST", headers: { - Authorization: `Basic ${GIGA_CHAT_ACCESS_TOKEN}`, + Authorization: `Basic ${gigaToken}`, "Content-Type": "application/x-www-form-urlencoded", Accept: "application/json", RqUID: rqUID, @@ -343,14 +346,30 @@ const analyzeImage = async (fileId, token) => { update_interval: 0, messages: [ { - role: "user", + role: "system", content: - `Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения. Ответ должен содержать: 1. Оценку степени загрязнения в виде числа от 0 до 10, где 0 - без загрязнений, 10 - загрязнена на 100%. 2. Пояснение к оценке в виде абзаца текста с обоснованием выставленной оценки. - Ответ должен быть в виде объекта следующего вида: - { - "value": число от 0 до 10, - "description": "текст с обоснованием оценки" - }`, + `Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения. +ВАЖНО: Твой ответ ДОЛЖЕН быть СТРОГО в формате JSON и содержать ТОЛЬКО следующие поля: +{ +"value": число от 0 до 10 (целое или с одним знаком после запятой), +"description": "текстовое описание на русском языке" +} +Правила: +1. Поле "value": +- Должно быть числом от 0 до 10 +- 0 = машина абсолютно чистая +- 10 = машина максимально грязная +2. Поле "description": +- Должно содержать 2-3 предложения на русском языке +- Обязательно указать конкретные признаки загрязнения +- Объяснить почему выставлен именно такой балл +НЕ ДОБАВЛЯЙ никаких дополнительных полей или комментариев вне JSON структуры. +НЕ ИСПОЛЬЗУЙ markdown форматирование. +ОТВЕТ ДОЛЖЕН БЫТЬ ВАЛИДНЫМ JSON.`, + }, + { + role: "user", + content: 'Дай оценку для приложенного файла изображения согласно структуре, ответ должен быть на русском языке', attachments: [fileId], }, ], @@ -358,7 +377,12 @@ const analyzeImage = async (fileId, token) => { }) const data = await response.json() - return JSON.parse(data.choices[0].message.content) + try { + return JSON.parse(data.choices[0].message.content) + } catch (error) { + console.error(error) + return { description: data.choices[0].message.content } + } } const convertFileToBase64 = (file) => { @@ -366,6 +390,8 @@ const convertFileToBase64 = (file) => { return base64Image } +process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0" + router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => { const { id: orderId } = req.params if (!mongoose.Types.ObjectId.isValid(orderId)) { @@ -380,20 +406,24 @@ router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => { throw new Error(VALIDATION_MESSAGES.carImg.required) } - const { access_token } = await getToken(req, res) + try { + const { access_token } = await getToken(req, res) - const fileId = await uploadImage(req.file, access_token) - const { value, description } = await analyzeImage(fileId, access_token) ?? {} + const fileId = await uploadImage(req.file, access_token) + const { value, description } = await analyzeImage(fileId, access_token) ?? {} - const orderCarImg = await OrderCarImgModel.create({ - image: convertFileToBase64(req.file), - imageRating: value, - imageDescription: description, - orderId, - created: new Date().toISOString(), - }) + const orderCarImg = await OrderCarImgModel.create({ + image: convertFileToBase64(req.file), + imageRating: value, + imageDescription: description, + orderId, + created: new Date().toISOString(), + }) - res.status(200).send({ success: true, body: orderCarImg }) + res.status(200).send({ success: true, body: orderCarImg }) + } catch (error) { + console.error(error) + } }) router.use((err, req, res, next) => { From 9a0669df13ee9d05b907c15211e94f1762006053 Mon Sep 17 00:00:00 2001 From: ilnaz <237x237@gmail.com> Date: Mon, 3 Mar 2025 20:08:28 +0300 Subject: [PATCH 28/41] feat: add find by id --- server/routers/dry-wash/order.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index 8244900..7485b75 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -156,17 +156,21 @@ router.post('/create', async (req, res, next) => { router.get('/:id', async (req, res, next) => { const { id } = req.params + if (!mongoose.Types.ObjectId.isValid(id)) { throw new Error(VALIDATION_MESSAGES.orderId.invalid) } try { const order = await OrderModel.findById(id) + if (!order) { throw new Error(VALIDATION_MESSAGES.order.notFound) } - res.status(200).send({ success: true, body: order }) + const data = await OrderCarImgModel.findOne({ orderId: order.id }) || {} + + res.status(200).send({ success: true, body: {...order,...data} }) } catch (error) { next(error) } @@ -277,7 +281,6 @@ const upload = multer({ const { v4: uuidv4 } = require("uuid") const axios = require('axios') -const GIGA_CHAT_ACCESS_TOKEN = 'MTQwMmNmZjgtZjA5OC00OGMxLWI0OTUtNWU3ZTU4YzMzZjdjOmU5OGFiYmNiLThmMDItNGVmOC1hNjhhLTA4Y2QxYjVmOGRmMA==' const GIGA_CHAT_OAUTH = 'https://ngw.devices.sberbank.ru:9443/api/v2/oauth' const GIGA_CHAT_API = 'https://gigachat.devices.sberbank.ru/api/v1' @@ -416,7 +419,7 @@ router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => { image: convertFileToBase64(req.file), imageRating: value, imageDescription: description, - orderId, + orderId: order.id, created: new Date().toISOString(), }) @@ -439,4 +442,4 @@ router.use((err, req, res, next) => { throw new Error(err.message) }) -module.exports = router \ No newline at end of file +module.exports = router From 350d452a7b1b3d4dd2515fa6fa8b1e8562601dc4 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Mon, 3 Mar 2025 20:13:51 +0300 Subject: [PATCH 29/41] fix file convert to base64 --- server/routers/dry-wash/order.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index 8244900..6832117 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -387,7 +387,7 @@ const analyzeImage = async (fileId, token) => { const convertFileToBase64 = (file) => { const base64Image = file.buffer.toString('base64') - return base64Image + return `data:${file.mimetype};base64,${base64Image}` } process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = "0" From 87fd3121f9bec0e536347e45b255b1a33bfbac1f Mon Sep 17 00:00:00 2001 From: RustamRu Date: Mon, 3 Mar 2025 20:41:55 +0300 Subject: [PATCH 30/41] fix: get img value data --- server/routers/dry-wash/order.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index 722bbec..f8aa9a6 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -168,9 +168,9 @@ router.get('/:id', async (req, res, next) => { throw new Error(VALIDATION_MESSAGES.order.notFound) } - const data = await OrderCarImgModel.findOne({ orderId: order.id }) || {} + const imgProps = await OrderCarImgModel.findOne({ orderId: order.id }) - res.status(200).send({ success: true, body: {...order,...data} }) + res.status(200).send({ success: true, body: { ...order.toObject(), ...imgProps?.toObject() } }) } catch (error) { next(error) } @@ -410,6 +410,8 @@ router.post('/:id/upload-car-img', upload.single('file'), async (req, res) => { } try { + await OrderCarImgModel.deleteMany({ orderId }) + const { access_token } = await getToken(req, res) const fileId = await uploadImage(req.file, access_token) From 70e8a6877cac46088a80eda0a6e38d9c0eca2540 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Tue, 4 Mar 2025 19:04:38 +0300 Subject: [PATCH 31/41] change car img size limit --- server/routers/dry-wash/order.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index f8aa9a6..62eaef5 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -30,7 +30,7 @@ const isValidOrderStatus = (value) => Object.values(orderStatus).includes(value) const isValidOrderNotes = (value) => value.length < 500 const allowedMimeTypes = ['image/jpeg', 'image/png'] -const sizeLimitInMegaBytes = 5 +const sizeLimitInMegaBytes = 15 const VALIDATION_MESSAGES = { order: { From 7b685ad99ed7d2be58cf0ff9cd0e5ef4bddbb676 Mon Sep 17 00:00:00 2001 From: RustamRu Date: Sun, 9 Mar 2025 11:04:01 +0300 Subject: [PATCH 32/41] feat: add dynamic system prompt for car image analysis --- server/routers/dry-wash/get-token.js | 9 ++++++++- server/routers/dry-wash/order.js | 22 ++-------------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/server/routers/dry-wash/get-token.js b/server/routers/dry-wash/get-token.js index 8543dc6..77e83e0 100644 --- a/server/routers/dry-wash/get-token.js +++ b/server/routers/dry-wash/get-token.js @@ -4,6 +4,13 @@ const getGigaToken = async () => { return data.features['dry-wash-bh'].GIGA_TOKEN.value } +const getSystemPrompt = async () => { + const response = await fetch('https://admin.bro-js.ru/api/config/v1/dev') + const data = await response.json() + return data.features['dry-wash-bh'].SYSTEM_PROMPT.value +} + module.exports = { - getGigaToken + getGigaToken, + getSystemPrompt } \ No newline at end of file diff --git a/server/routers/dry-wash/order.js b/server/routers/dry-wash/order.js index 62eaef5..746ad6c 100644 --- a/server/routers/dry-wash/order.js +++ b/server/routers/dry-wash/order.js @@ -5,7 +5,7 @@ const { MasterModel } = require('./model/master') const { OrderModel } = require('./model/order') const { OrderCarImgModel } = require('./model/order.car-img') const { orderStatus } = require('./model/const') -const { getGigaToken } = require('./get-token') +const { getGigaToken, getSystemPrompt } = require('./get-token') const isValidPhoneNumber = (value) => /^(\+)?\d{9,15}/.test(value) const isValidCarNumber = (value) => /^[авекмнорстух][0-9]{3}[авекмнорстух]{2}[0-9]{2,3}$/i.test(value) @@ -350,25 +350,7 @@ const analyzeImage = async (fileId, token) => { messages: [ { role: "system", - content: - `Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения. -ВАЖНО: Твой ответ ДОЛЖЕН быть СТРОГО в формате JSON и содержать ТОЛЬКО следующие поля: -{ -"value": число от 0 до 10 (целое или с одним знаком после запятой), -"description": "текстовое описание на русском языке" -} -Правила: -1. Поле "value": -- Должно быть числом от 0 до 10 -- 0 = машина абсолютно чистая -- 10 = машина максимально грязная -2. Поле "description": -- Должно содержать 2-3 предложения на русском языке -- Обязательно указать конкретные признаки загрязнения -- Объяснить почему выставлен именно такой балл -НЕ ДОБАВЛЯЙ никаких дополнительных полей или комментариев вне JSON структуры. -НЕ ИСПОЛЬЗУЙ markdown форматирование. -ОТВЕТ ДОЛЖЕН БЫТЬ ВАЛИДНЫМ JSON.`, + content: (await getSystemPrompt()) ?? `Ты эксперт по оценке степени загрязнения автомобилей. Твоя задача — анализировать фотографии машин и определять степень их загрязнения. ВАЖНО: Твой ответ ДОЛЖЕН быть СТРОГО в формате JSON и содержать ТОЛЬКО следующие поля: { "value": число от 0 до 10 (целое или с одним знаком после запятой), "description": "текстовое описание на русском языке" } Правила: 1. Поле "value": - Должно быть числом от 0 до 10 - 0 = машина абсолютно чистая - 10 = машина максимально грязная 2. Поле "description": - Должно содержать 2-3 предложения на русском языке - Обязательно указать конкретные признаки загрязнения - Объяснить почему выставлен именно такой балл НЕ ДОБАВЛЯЙ никаких дополнительных полей или комментариев вне JSON структуры. НЕ ИСПОЛЬЗУЙ markdown форматирование. ОТВЕТ ДОЛЖЕН БЫТЬ ВАЛИДНЫМ JSON. Если на фотографии нет одной машины, то оценка должна быть 0 и в описании должно быть указано, почему не удалось оценить.`, }, { role: "user", From 1fcc5ed70d525910687c9b7c046252cf12cd581b Mon Sep 17 00:00:00 2001 From: Primakov Alexandr Alexandrovich Date: Tue, 11 Mar 2025 23:50:50 +0300 Subject: [PATCH 33/41] init Questionnaire --- server/index.js | 1 + server/models/questionnaire.js | 60 + server/routers/questioneer/index.js | 421 ++++ server/routers/questioneer/public/admin.html | 70 + server/routers/questioneer/public/create.html | 129 ++ server/routers/questioneer/public/edit.html | 146 ++ server/routers/questioneer/public/index.html | 42 + server/routers/questioneer/public/poll.html | 45 + .../questioneer/public/static/css/style.css | 1830 +++++++++++++++++ .../questioneer/public/static/js/admin.js | 294 +++ .../questioneer/public/static/js/common.js | 236 +++ .../questioneer/public/static/js/create.js | 343 +++ .../questioneer/public/static/js/edit.js | 332 +++ .../questioneer/public/static/js/index.js | 67 + .../questioneer/public/static/js/poll.js | 1069 ++++++++++ 15 files changed, 5085 insertions(+) create mode 100644 server/models/questionnaire.js create mode 100644 server/routers/questioneer/index.js create mode 100644 server/routers/questioneer/public/admin.html create mode 100644 server/routers/questioneer/public/create.html create mode 100644 server/routers/questioneer/public/edit.html create mode 100644 server/routers/questioneer/public/index.html create mode 100644 server/routers/questioneer/public/poll.html create mode 100644 server/routers/questioneer/public/static/css/style.css create mode 100644 server/routers/questioneer/public/static/js/admin.js create mode 100644 server/routers/questioneer/public/static/js/common.js create mode 100644 server/routers/questioneer/public/static/js/create.js create mode 100644 server/routers/questioneer/public/static/js/edit.js create mode 100644 server/routers/questioneer/public/static/js/index.js create mode 100644 server/routers/questioneer/public/static/js/poll.js diff --git a/server/index.js b/server/index.js index 1e81309..2a179cd 100644 --- a/server/index.js +++ b/server/index.js @@ -90,6 +90,7 @@ app.use("/dhs-testing", require("./routers/dhs-testing")) app.use("/gamehub", require("./routers/gamehub")) app.use("/esc", require("./routers/esc")) app.use('/connectme', require('./routers/connectme')) +app.use('/questioneer', require('./routers/questioneer')) app.use(require("./error")) diff --git a/server/models/questionnaire.js b/server/models/questionnaire.js new file mode 100644 index 0000000..e08928e --- /dev/null +++ b/server/models/questionnaire.js @@ -0,0 +1,60 @@ +const mongoose = require('mongoose'); + +// Типы вопросов +const QUESTION_TYPES = { + SINGLE_CHOICE: 'single_choice', // Один вариант + MULTIPLE_CHOICE: 'multiple_choice', // Несколько вариантов + TEXT: 'text', // Текстовый ответ + RATING: 'rating', // Оценка по шкале + TAG_CLOUD: 'tag_cloud' // Облако тегов +}; + +// Типы отображения +const DISPLAY_TYPES = { + DEFAULT: 'default', + TAG_CLOUD: 'tag_cloud', + VOTING: 'voting', + POLL: 'poll' +}; + +// Схема варианта ответа +const optionSchema = new mongoose.Schema({ + text: { type: String, required: true }, + count: { type: Number, default: 0 } // счетчик голосов +}); + +// Схема вопроса +const questionSchema = new mongoose.Schema({ + text: { type: String, required: true }, + type: { + type: String, + enum: Object.values(QUESTION_TYPES), + required: true + }, + options: [optionSchema], + required: { type: Boolean, default: false } +}); + +// Схема опроса +const questionnaireSchema = new mongoose.Schema({ + title: { type: String, required: true }, + description: { type: String }, + questions: [questionSchema], + displayType: { + type: String, + enum: Object.values(DISPLAY_TYPES), + default: DISPLAY_TYPES.DEFAULT + }, + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now }, + adminLink: { type: String, required: true }, // ссылка для редактирования + publicLink: { type: String, required: true } // ссылка для голосования +}); + +const Questionnaire = mongoose.model('Questionnaire', questionnaireSchema); + +module.exports = { + Questionnaire, + QUESTION_TYPES, + DISPLAY_TYPES +}; \ No newline at end of file diff --git a/server/routers/questioneer/index.js b/server/routers/questioneer/index.js new file mode 100644 index 0000000..c031883 --- /dev/null +++ b/server/routers/questioneer/index.js @@ -0,0 +1,421 @@ +const express = require('express') +const { Router } = require("express") +const router = Router() +const crypto = require('crypto') +const path = require('path') +const { getDB } = require('../../utils/mongo') +const mongoose = require('mongoose') + +// Используем одно определение модели +const Questionnaire = (() => { + // Если модель уже существует, используем её + if (mongoose.models.Questionnaire) { + return mongoose.models.Questionnaire; + } + + // Иначе создаем новую модель + const questionnaireSchema = new mongoose.Schema({ + title: { type: String, required: true }, + description: { type: String }, + questions: [{ + text: { type: String, required: true }, + type: { + type: String, + enum: ['single_choice', 'multiple_choice', 'text', 'rating', 'tag_cloud', 'scale'], + required: true + }, + required: { type: Boolean, default: false }, + options: [{ + text: { type: String, required: true }, + count: { type: Number, default: 0 } + }], + scaleMin: { type: Number }, + scaleMax: { type: Number }, + scaleMinLabel: { type: String }, + scaleMaxLabel: { type: String }, + answers: [{ type: String }], + scaleValues: [{ type: Number }], + tags: [{ + text: { type: String }, + count: { type: Number, default: 1 } + }] + }], + displayType: { + type: String, + enum: ['default', 'tag_cloud', 'voting', 'poll', 'step_by_step'], + default: 'step_by_step' + }, + createdAt: { type: Date, default: Date.now }, + updatedAt: { type: Date, default: Date.now }, + adminLink: { type: String, required: true }, + publicLink: { type: String, required: true } + }); + + return mongoose.model('Questionnaire', questionnaireSchema); +})(); + +// Middleware для парсинга JSON +router.use(express.json()); + +// Обслуживание статичных файлов - проверяем правильность пути +router.use('/static', express.static(path.join(__dirname, 'public', 'static'))); + +// Получить главную страницу +router.get("/", (req, res) => { + res.sendFile(path.join(__dirname, 'public/index.html')) +}) + +// Страница создания нового опроса +router.get("/create", (req, res) => { + res.sendFile(path.join(__dirname, 'public/create.html')) +}) + +// Страница редактирования опроса +router.get("/edit/:adminLink", (req, res) => { + res.sendFile(path.join(__dirname, 'public/edit.html')) +}) + +// Страница администрирования опроса +router.get("/admin/:adminLink", (req, res) => { + res.sendFile(path.join(__dirname, 'public/admin.html')) +}) + +// Страница голосования +router.get("/poll/:publicLink", (req, res) => { + res.sendFile(path.join(__dirname, 'public/poll.html')) +}) + +// API для работы с опросами + +// Создать новый опрос +router.post("/api/questionnaires", async (req, res) => { + try { + // Проверка наличия нужных полей + const { title, questions } = req.body; + + if (!title || !Array.isArray(questions) || questions.length === 0) { + return res.json({ success: false, error: 'Необходимо указать название и хотя бы один вопрос' }); + } + + // Создаем уникальные ссылки + const adminLink = crypto.randomBytes(6).toString('hex'); + const publicLink = crypto.randomBytes(6).toString('hex'); + + // Устанавливаем тип отображения step_by_step, если не указан + if (!req.body.displayType) { + req.body.displayType = 'step_by_step'; + } + + // Создаем новый опросник + const questionnaire = new Questionnaire({ + ...req.body, + adminLink, + publicLink + }); + + await questionnaire.save(); + + res.json({ + success: true, + data: { + adminLink, + publicLink + } + }); + } catch (error) { + console.error('Error creating questionnaire:', error); + res.json({ success: false, error: error.message }); + } +}); + +// Получить все опросы +router.get("/api/questionnaires", async (req, res) => { + try { + const questionnaires = await Questionnaire.find({}, { + title: 1, + description: 1, + createdAt: 1, + updatedAt: 1, + _id: 1, + adminLink: 1, + publicLink: 1 + }).sort({ createdAt: -1 }) + + res.status(200).json({ + success: true, + data: questionnaires + }) + } catch (error) { + console.error('Error fetching questionnaires:', error) + res.status(500).json({ + success: false, + error: 'Failed to fetch questionnaires' + }) + } +}) + +// Получить опрос по ID для админа +router.get("/api/questionnaires/admin/:adminLink", async (req, res) => { + try { + const { adminLink } = req.params + const questionnaire = await Questionnaire.findOne({ adminLink }) + + if (!questionnaire) { + return res.status(404).json({ + success: false, + error: 'Questionnaire not found' + }) + } + + res.status(200).json({ + success: true, + data: questionnaire + }) + } catch (error) { + console.error('Error fetching questionnaire:', error) + res.status(500).json({ + success: false, + error: 'Failed to fetch questionnaire' + }) + } +}) + +// Получить опрос по публичной ссылке (для голосования) +router.get("/api/questionnaires/public/:publicLink", async (req, res) => { + try { + const { publicLink } = req.params + const questionnaire = await Questionnaire.findOne({ publicLink }) + + if (!questionnaire) { + return res.status(404).json({ + success: false, + error: 'Questionnaire not found' + }) + } + + res.status(200).json({ + success: true, + data: questionnaire + }) + } catch (error) { + console.error('Error fetching questionnaire:', error) + res.status(500).json({ + success: false, + error: 'Failed to fetch questionnaire' + }) + } +}) + +// Обновить опрос +router.put("/api/questionnaires/:adminLink", async (req, res) => { + try { + const { adminLink } = req.params + const { title, description, questions, displayType } = req.body + + const updatedQuestionnaire = await Questionnaire.findOneAndUpdate( + { adminLink }, + { + title, + description, + questions, + displayType, + updatedAt: Date.now() + }, + { new: true } + ) + + if (!updatedQuestionnaire) { + return res.status(404).json({ + success: false, + error: 'Questionnaire not found' + }) + } + + res.status(200).json({ + success: true, + data: updatedQuestionnaire + }) + } catch (error) { + console.error('Error updating questionnaire:', error) + res.status(500).json({ + success: false, + error: 'Failed to update questionnaire' + }) + } +}) + +// Удалить опрос +router.delete("/api/questionnaires/:adminLink", async (req, res) => { + try { + const { adminLink } = req.params + + const deletedQuestionnaire = await Questionnaire.findOneAndDelete({ adminLink }) + + if (!deletedQuestionnaire) { + return res.status(404).json({ + success: false, + error: 'Questionnaire not found' + }) + } + + res.status(200).json({ + success: true, + message: 'Questionnaire deleted successfully' + }) + } catch (error) { + console.error('Error deleting questionnaire:', error) + res.status(500).json({ + success: false, + error: 'Failed to delete questionnaire' + }) + } +}) + +// Голосование в опросе +router.post("/api/vote/:publicLink", async (req, res) => { + try { + const { publicLink } = req.params + const { answers } = req.body + + const questionnaire = await Questionnaire.findOne({ publicLink }) + + if (!questionnaire) { + return res.status(404).json({ + success: false, + error: 'Questionnaire not found' + }) + } + + // Обновить счетчики голосов + answers.forEach(answer => { + const { questionIndex, optionIndices, textAnswer, scaleValue, tagTexts } = answer + + // Обработка одиночного и множественного выбора + if (Array.isArray(optionIndices)) { + // Для множественного выбора + optionIndices.forEach(optionIndex => { + if (questionnaire.questions[questionIndex] && + questionnaire.questions[questionIndex].options[optionIndex]) { + questionnaire.questions[questionIndex].options[optionIndex].count += 1 + } + }) + } else if (typeof optionIndices === 'number') { + // Для единичного выбора + if (questionnaire.questions[questionIndex] && + questionnaire.questions[questionIndex].options[optionIndices]) { + questionnaire.questions[questionIndex].options[optionIndices].count += 1 + } + } + + // Сохраняем текстовые ответы + if (textAnswer && questionnaire.questions[questionIndex]) { + if (!questionnaire.questions[questionIndex].answers) { + questionnaire.questions[questionIndex].answers = []; + } + questionnaire.questions[questionIndex].answers.push(textAnswer); + } + + // Сохраняем ответы шкалы оценки + if (scaleValue !== undefined && questionnaire.questions[questionIndex]) { + if (!questionnaire.questions[questionIndex].scaleValues) { + questionnaire.questions[questionIndex].scaleValues = []; + } + questionnaire.questions[questionIndex].scaleValues.push(scaleValue); + } + + // Сохраняем теги + if (Array.isArray(tagTexts) && tagTexts.length > 0 && questionnaire.questions[questionIndex]) { + if (!questionnaire.questions[questionIndex].tags) { + questionnaire.questions[questionIndex].tags = []; + } + + tagTexts.forEach(tagText => { + const existingTag = questionnaire.questions[questionIndex].tags.find(t => t.text === tagText); + if (existingTag) { + existingTag.count += 1; + } else { + questionnaire.questions[questionIndex].tags.push({ text: tagText, count: 1 }); + } + }); + } + }) + + await questionnaire.save() + + res.status(200).json({ + success: true, + message: 'Vote registered successfully' + }) + } catch (error) { + console.error('Error registering vote:', error) + res.status(500).json({ + success: false, + error: 'Failed to register vote' + }) + } +}) + +// Получить результаты опроса по публичной ссылке +router.get("/api/results/:publicLink", async (req, res) => { + try { + const { publicLink } = req.params; + const questionnaire = await Questionnaire.findOne({ publicLink }); + + if (!questionnaire) { + return res.status(404).json({ + success: false, + error: 'Questionnaire not found' + }); + } + + // Формируем результаты для отправки + const results = { + title: questionnaire.title, + description: questionnaire.description, + questions: questionnaire.questions.map(question => { + const result = { + text: question.text, + type: question.type + }; + + // Добавляем варианты ответов, если они есть + if (question.options && question.options.length > 0) { + result.options = question.options; + } + + // Добавляем текстовые ответы, если они есть + if (question.answers && question.answers.length > 0) { + result.answers = question.answers; + } + + // Добавляем результаты шкалы, если они есть + if (question.scaleValues && question.scaleValues.length > 0) { + result.scaleValues = question.scaleValues; + + // Считаем среднее значение + result.scaleAverage = question.scaleValues.reduce((a, b) => a + b, 0) / question.scaleValues.length; + } + + // Добавляем теги, если они есть + if (question.tags && question.tags.length > 0) { + result.tags = question.tags; + } + + return result; + }) + }; + + res.status(200).json({ + success: true, + data: results + }); + } catch (error) { + console.error('Error fetching poll results:', error); + res.status(500).json({ + success: false, + error: 'Failed to fetch poll results' + }); + } +}); + +module.exports = router diff --git a/server/routers/questioneer/public/admin.html b/server/routers/questioneer/public/admin.html new file mode 100644 index 0000000..5928fc2 --- /dev/null +++ b/server/routers/questioneer/public/admin.html @@ -0,0 +1,70 @@ + + + + + + Управление опросом + + + + + + +
+

Управление опросом

+ +
Загрузка опроса...
+ + +
+ + + + + + + \ No newline at end of file diff --git a/server/routers/questioneer/public/create.html b/server/routers/questioneer/public/create.html new file mode 100644 index 0000000..61cddc9 --- /dev/null +++ b/server/routers/questioneer/public/create.html @@ -0,0 +1,129 @@ + + + + + + Создание нового опроса + + + +
+

Создание нового опроса

+ +
+
+
+ + +
+ +
+ + +
+ + + +
+

Вопросы

+
+ + +
+ +
+ Отмена + +
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/server/routers/questioneer/public/edit.html b/server/routers/questioneer/public/edit.html new file mode 100644 index 0000000..a67890f --- /dev/null +++ b/server/routers/questioneer/public/edit.html @@ -0,0 +1,146 @@ + + + + + + Редактирование опроса + + + +
+

Редактирование опроса

+ +
Загрузка опроса...
+ + +
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/server/routers/questioneer/public/index.html b/server/routers/questioneer/public/index.html new file mode 100644 index 0000000..ea8b6ef --- /dev/null +++ b/server/routers/questioneer/public/index.html @@ -0,0 +1,42 @@ + + + + + + Анонимные опросы + + + + + + +
+

Сервис анонимных опросов

+ + + +
+

Ваши опросы

+
+

Загрузка опросов...

+
+
+
+ + + + + + \ No newline at end of file diff --git a/server/routers/questioneer/public/poll.html b/server/routers/questioneer/public/poll.html new file mode 100644 index 0000000..0aa8463 --- /dev/null +++ b/server/routers/questioneer/public/poll.html @@ -0,0 +1,45 @@ + + + + + + Участие в опросе + + + +
+
Загрузка опроса...
+ + +
+ + + + + + \ No newline at end of file diff --git a/server/routers/questioneer/public/static/css/style.css b/server/routers/questioneer/public/static/css/style.css new file mode 100644 index 0000000..5dc4bcf --- /dev/null +++ b/server/routers/questioneer/public/static/css/style.css @@ -0,0 +1,1830 @@ +/* Темный стиль */ +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; + margin: 0; + padding: 0; + color: #e0e0e0; + background-color: #1e1e1e; + line-height: 1.6; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0.5rem; + font-weight: 500; + line-height: 1.2; +} + +h1 { + font-size: 2.5rem; + margin-bottom: 1.5rem; + color: #61dafb; +} + +h2 { + font-size: 2rem; + color: #64b5f6; +} + +h3 { + font-size: 1.5rem; + color: #81c784; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +/* Кнопки */ +.btn { + display: inline-block; + font-weight: 400; + text-align: center; + vertical-align: middle; + user-select: none; + padding: 0.5rem 1rem; + font-size: 1rem; + line-height: 1.5; + border-radius: 0.25rem; + text-decoration: none; + cursor: pointer; + background-color: #2196f3; + color: white; + border: 1px solid transparent; + transition: all 0.15s ease-in-out; +} + +.btn:hover { + background-color: #1976d2; +} + +.btn-primary { + background-color: #4caf50; +} + +.btn-primary:hover { + background-color: #388e3c; +} + +.btn-secondary { + background-color: #757575; +} + +.btn-secondary:hover { + background-color: #616161; +} + +.btn-danger { + background-color: #f44336; +} + +.btn-danger:hover { + background-color: #d32f2f; +} + +.btn-small { + padding: 0.25rem 0.5rem; + font-size: 0.875rem; +} + +.btn-icon { + background: none; + border: none; + cursor: pointer; + font-size: 1rem; + padding: 0; + margin: 0 5px; + color: #e0e0e0; +} + +/* Формы */ +.form-container { + background-color: #2d2d2d; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + margin-bottom: 20px; +} + +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: 500; + color: #bbdefb; +} + +input[type="text"], +input[type="email"], +input[type="password"], +input[type="number"], +textarea, +select { + display: block; + width: 100%; + padding: 0.5rem 0.75rem; + font-size: 1rem; + line-height: 1.5; + color: #e0e0e0; + background-color: #424242; + background-clip: padding-box; + border: 1px solid #616161; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + box-sizing: border-box; +} + +input:focus, +textarea:focus, +select:focus { + color: #e0e0e0; + background-color: #424242; + border-color: #64b5f6; + outline: 0; + box-shadow: 0 0 0 0.2rem rgba(33, 150, 243, 0.25); +} + +.form-actions { + margin-top: 20px; + display: flex; + justify-content: space-between; +} + +/* Список опросов */ +.questionnaires-list { + margin-top: 30px; +} + +.questionnaire-item { + background-color: #2d2d2d; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + margin-bottom: 20px; +} + +.questionnaire-links { + display: flex; + gap: 10px; + margin-top: 15px; +} + +/* Вопросы */ +.question-item { + background-color: #2d2d2d; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + margin-bottom: 20px; +} + +.question-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.options-container { + margin-top: 10px; +} + +.options-list { + margin-bottom: 10px; +} + +.option-item { + display: flex; + align-items: center; + margin-bottom: 8px; +} + +/* Стили для варианта ответа */ +.option-item input[type="text"] { + flex: 1; + margin-right: 10px; +} + +.radio-option label, +.checkbox-option label { + margin-left: 10px; + cursor: pointer; +} + +/* Звездный рейтинг */ +.rating-container { + display: flex; + gap: 10px; + margin-top: 10px; +} + +.rating-item { + display: flex; + flex-direction: column; + align-items: center; +} + +.rating-item label { + cursor: pointer; + padding: 8px 12px; + background-color: #424242; + border-radius: 4px; + transition: all 0.2s; +} + +.rating-item input { + display: none; +} + +.rating-item input:checked + label { + background-color: #2196f3; + color: white; +} + +/* Облако тегов */ +.tag-cloud-container { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 10px; +} + +.tag-item { + padding: 8px 15px; + background-color: #424242; + border-radius: 20px; + cursor: pointer; + transition: all 0.2s; +} + +.tag-item:hover { + background-color: #616161; +} + +.tag-item.selected { + background-color: #2196f3; + color: white; +} + +/* Результаты голосования */ +.results-visualization { + margin-top: 15px; +} + +.result-bar-container { + margin-bottom: 15px; +} + +.result-label { + margin-bottom: 5px; + font-weight: 500; + color: #bbdefb; +} + +.result-bar { + height: 20px; + background-color: #424242; + border-radius: 4px; + overflow: hidden; +} + +.result-bar-fill { + height: 100%; + background-color: #2196f3; + border-radius: 4px; + transition: width 0.5s; +} + +.result-percent { + margin-top: 5px; + font-size: 0.875rem; + color: #9e9e9e; +} + +.results-tag-cloud { + display: flex; + flex-wrap: wrap; + gap: 15px; + margin-top: 15px; + justify-content: center; +} + +.result-tag { + padding: 5px 10px; + background-color: #2196f3; + color: white; + border-radius: 20px; + display: inline-block; +} + +/* Обязательные поля */ +.required-mark { + color: #f44336; +} + +/* Состояния загрузки и ошибки */ +#loading { + text-align: center; + padding: 20px; + font-size: 1.2rem; + color: #9e9e9e; +} + +.error { + color: #f44336; + padding: 10px; + background-color: rgba(244, 67, 54, 0.2); + border-radius: 4px; +} + +/* Ссылки */ +.link-group { + margin-bottom: 15px; +} + +.link-input-group { + display: flex; + gap: 10px; +} + +.link-input-group input { + flex: 1; +} + +/* Текстовая область для ввода ответа */ +.textarea-container { + width: 100%; + margin-top: 10px; +} + +.text-answer { + width: 100%; + min-height: 100px; + resize: vertical; +} + +/* Таблица статистики */ +.stats-table { + width: 100%; + border-collapse: collapse; + margin-top: 10px; + color: #e0e0e0; +} + +.stats-table th, +.stats-table td { + padding: 8px; + text-align: left; + border-bottom: 1px solid #424242; +} + +.stats-table th { + background-color: #383838; + font-weight: 500; + color: #bbdefb; +} + +.stats-table .total-row { + font-weight: bold; + background-color: #424242; +} + +/* Стили для question-stats */ +.question-stats { + background-color: #2d2d2d; + padding: 15px; + border-radius: 5px; + margin-bottom: 20px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); +} + +/* Модальные окна */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s, visibility 0.3s; +} + +.modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.modal { + background-color: #2d2d2d; + border-radius: 5px; + box-shadow: 0 3px 15px rgba(0, 0, 0, 0.3); + width: 90%; + max-width: 500px; + padding: 20px; + position: relative; + transform: translateY(-20px); + transition: transform 0.3s; + max-height: 90vh; + overflow-y: auto; +} + +.modal-overlay.active .modal { + transform: translateY(0); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #444; +} + +.modal-header h3 { + margin: 0; + color: #64b5f6; +} + +.modal-close { + background: none; + border: none; + color: #e0e0e0; + font-size: 1.5rem; + cursor: pointer; + padding: 0; + line-height: 1; +} + +.modal-body { + margin-bottom: 20px; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 10px; + padding-top: 10px; + border-top: 1px solid #444; +} + +/* QR код */ +.qr-container { + display: flex; + flex-direction: column; + align-items: center; + padding: 15px; +} + +.qr-code { + margin-bottom: 15px; + background-color: #fff; + padding: 15px; + border-radius: 8px; +} + +.qr-link-container { + display: flex; + width: 100%; + max-width: 500px; + margin-top: 10px; +} + +.qr-link-input { + flex-grow: 1; + padding: 10px; + border: 1px solid #444; + border-radius: 4px 0 0 4px; + background-color: #333; + color: #fff; +} + +.btn-copy-link { + padding: 10px 15px; + background-color: #64b5f6; + color: #fff; + border: none; + border-radius: 0 4px 4px 0; + cursor: pointer; + transition: background-color 0.3s; +} + +.btn-copy-link:hover { + background-color: #90caf9; +} + +.btn-copy-link.copied { + background-color: #4caf50; +} + +/* Шкала оценки */ +.scale-container { + margin-top: 20px; + width: 100%; +} + +.scale-labels { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} + +.scale-label-min, +.scale-label-max { + font-weight: 500; + color: #bbdefb; +} + +.scale-values { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 5px; +} + +.scale-item { + text-align: center; +} + +.scale-item input { + display: none; +} + +.scale-item label { + display: block; + width: 40px; + height: 40px; + line-height: 40px; + text-align: center; + background-color: #424242; + border-radius: 50%; + cursor: pointer; + transition: all 0.2s; +} + +.scale-item input:checked + label { + background-color: #2196f3; + color: white; +} + +.scale-item label:hover { + background-color: #616161; +} + +/* Пошаговый опрос */ +.step-navigation { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 20px; + padding-top: 15px; + border-top: 1px solid #444; +} + +#question-counter { + font-size: 1rem; + color: #bbdefb; +} + +/* Улучшения для облака тегов */ +.tag-input-container { + width: 100%; +} + +.tag-input { + width: 100%; + padding: 10px; + margin-bottom: 10px; + background-color: #424242; + border: 1px solid #616161; + border-radius: 4px; + color: #e0e0e0; +} + +.tag-items { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 10px; +} + +.tag-item { + position: relative; + padding: 8px 15px; + background-color: #424242; + border-radius: 20px; + cursor: pointer; + transition: all 0.2s; +} + +.tag-item.selected { + background-color: #2196f3; + color: white; +} + +.tag-remove { + margin-left: 5px; + font-size: 1.2rem; + cursor: pointer; +} + +/* Стили для результатов опроса */ +.question-result { + background-color: #2d2d2d; + padding: 15px; + border-radius: 5px; + margin-bottom: 20px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); +} + +.text-answers { + margin-top: 10px; +} + +.text-answer { + background-color: #424242; + padding: 10px; + border-radius: 4px; + margin-bottom: 10px; +} + +/* Улучшения для радио и чекбоксов */ +.radio-options-container, +.checkbox-options-container { + margin-top: 10px; +} + +.radio-option, +.checkbox-option { + display: flex; + align-items: center; + margin-bottom: 10px; +} + +.radio-option input, +.checkbox-option input { + margin-right: 10px; +} + +.radio-option label, +.checkbox-option label { + cursor: pointer; +} + +/* Стили для вопросов с ошибками */ +.question-item.error { + border-left: 4px solid var(--color-error); + box-shadow: 0 0 10px rgba(220, 53, 69, 0.3); + padding-left: 16px; + transition: all 0.3s ease; +} + +.question-item.error .question-title { + color: var(--color-error); +} + +/* Анимация для ошибок */ +.shake { + animation: shake 0.6s cubic-bezier(.36,.07,.19,.97) both; +} + +@keyframes shake { + 10%, 90% { + transform: translate3d(-1px, 0, 0); + } + + 20%, 80% { + transform: translate3d(2px, 0, 0); + } + + 30%, 50%, 70% { + transform: translate3d(-4px, 0, 0); + } + + 40%, 60% { + transform: translate3d(4px, 0, 0); + } +} + +/* Стили для выхода вопроса */ +.question-item.exit { + opacity: 0; + transform: translateX(-30px); + transition: opacity 0.3s ease, transform 0.3s ease; + pointer-events: none; +} + +/* Стили для плавного входа вопроса */ +.question-item.enter { + opacity: 0; + transform: translateX(30px); +} + +.question-item.active { + opacity: 1; + transform: translateX(0); + transition: opacity 0.5s ease, transform 0.5s ease; +} + +/* Анимации для элементов результатов */ +.results-title { + animation: slideDown 0.8s ease forwards; +} + +.scale-average { + margin-bottom: 15px; + font-size: 1.2em; +} + +.scale-average .highlight { + color: var(--color-primary); + font-size: 1.5em; + animation: pulse 2s infinite; +} + +.result-bar-container { + margin-bottom: 10px; +} + +.result-label { + margin-bottom: 5px; + font-weight: 500; +} + +.result-bar { + height: 20px; + background-color: rgba(0, 0, 0, 0.1); + border-radius: 4px; + overflow: hidden; + position: relative; +} + +.result-bar-fill { + height: 100%; + background-color: var(--color-primary); + transition: width 1s ease-out; + border-radius: 4px; +} + +.result-percent { + margin-top: 3px; + text-align: right; + font-size: 0.9em; + color: var(--color-muted); +} + +.text-answer { + background-color: rgba(0, 0, 0, 0.05); + padding: 10px 15px; + border-radius: 4px; + margin-bottom: 10px; + transition: opacity 0.5s ease, transform 0.5s ease; +} + +.results-tag-cloud { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: 20px 0; +} + +.result-tag { + background-color: rgba(var(--primary-rgb), 0.1); + color: var(--color-primary); + padding: 8px 15px; + border-radius: 20px; + margin: 5px; + display: inline-block; + transition: all 0.5s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateX(30px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes pulse { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + 100% { + transform: scale(1); + } +} + +.question-result { + transition: all 0.5s ease; + padding: 15px; + margin-bottom: 20px; + border-radius: 8px; + background-color: rgba(0, 0, 0, 0.03); +} + +/* Улучшенная анимация для счетчика вопросов */ +.question-counter { + transition: all 0.3s ease; + animation: fadeIn 0.5s; +} + +.question-counter.update { + animation: pulse 0.5s; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +/* Анимации для кнопок навигации */ +.nav-btn:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.nav-btn:active { + transform: translateY(-1px); + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +/* Улучшенные стили для загрузки */ +#loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin: 50px 0; +} + +.loading-spinner { + width: 50px; + height: 50px; + border: 5px solid rgba(0, 0, 0, 0.1); + border-radius: 50%; + border-top: 5px solid var(--color-primary); + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.loading-text { + margin-top: 15px; + animation: pulse 1.5s infinite; +} + +/* Анимации */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideInUp { + from { + transform: translateY(20px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +@keyframes slideInRight { + from { + transform: translateX(20px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes pulse { + 0% { transform: scale(1); } + 50% { transform: scale(1.05); } + 100% { transform: scale(1); } +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } + 20%, 40%, 60%, 80% { transform: translateX(5px); } +} + +@keyframes rotate { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* Применение анимаций */ +#questionnaire-container { + animation: fadeIn 0.6s ease-out; +} + +.question-item { + animation: slideInUp 0.5s ease-out; + transition: all 0.3s ease; +} + +.btn { + transition: all 0.2s ease-in-out; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn:active { + transform: translateY(1px); +} + +.btn-primary { + animation: pulse 2s infinite; +} + +.question-item.error { + animation: shake 0.5s ease-in-out; +} + +.radio-option label:hover, +.checkbox-option label:hover { + transform: translateX(3px); + transition: transform 0.2s ease; +} + +.tag-item { + transition: all 0.2s ease; +} + +.tag-item:hover { + transform: scale(1.05); +} + +.tag-item.selected { + animation: pulse 1s; +} + +.scale-item label:hover { + transform: scale(1.1); + transition: transform 0.2s ease; +} + +.scale-item input:checked + label { + animation: pulse 0.5s; +} + +/* Анимированный лоадер */ +#loading { + position: relative; + padding-left: 30px; +} + +#loading:before { + content: ''; + position: absolute; + left: 0; + top: 50%; + margin-top: -10px; + width: 20px; + height: 20px; + border: 3px solid #2196f3; + border-top-color: transparent; + border-radius: 50%; + animation: rotate 1s linear infinite; +} + +/* Анимированные переходы между вопросами */ +.step-navigation button { + transition: all 0.3s ease; +} + +.question-item.active { + animation: slideInRight 0.4s ease-out; +} + +.question-item.exit { + animation: fadeIn 0.4s ease-out reverse; +} + +/* Анимация для результатов */ +.result-bar-fill { + transition: width 1.5s ease-out; + animation: slideInRight 1.5s ease-out; +} + +.results-container { + animation: fadeIn 1s ease-out; +} + +.question-result { + animation: slideInUp 0.5s ease-out; + animation-fill-mode: both; +} + +.question-result:nth-child(1) { animation-delay: 0.1s; } +.question-result:nth-child(2) { animation-delay: 0.2s; } +.question-result:nth-child(3) { animation-delay: 0.3s; } +.question-result:nth-child(4) { animation-delay: 0.4s; } +.question-result:nth-child(5) { animation-delay: 0.5s; } +.question-result:nth-child(6) { animation-delay: 0.6s; } +.question-result:nth-child(7) { animation-delay: 0.7s; } +.question-result:nth-child(8) { animation-delay: 0.8s; } +.question-result:nth-child(9) { animation-delay: 0.9s; } +.question-result:nth-child(10) { animation-delay: 1s; } + +/* Другие улучшения стилей */ +.textarea-container textarea { + transition: height 0.3s ease; +} + +.textarea-container textarea:focus { + height: 120px; +} + +/* Анимация для модальных окон */ +.modal-overlay.active .modal { + animation: slideInUp 0.3s ease-out; +} + +/* Анимация для кнопки добавления вопроса */ +#add-question { + transition: background-color 0.3s ease, transform 0.2s ease; +} + +#add-question:hover { + transform: translateY(-2px); +} + +#add-question:active { + transform: translateY(1px); +} + +/* Анимация иконок */ +.btn-icon svg { + transition: transform 0.3s ease; +} + +.btn-icon:hover svg { + transform: rotate(90deg); +} + +/* Анимация для обратной связи */ +@keyframes success-animation { + 0% { background-color: transparent; } + 30% { background-color: rgba(76, 175, 80, 0.2); } + 100% { background-color: transparent; } +} + +.success-feedback { + animation: success-animation 1.5s ease; +} + +/* Анимированные переключатели */ +input[type="checkbox"], input[type="radio"] { + transition: all 0.2s ease; +} + +/* Дополнительные плавные переходы для всех элементов */ +* { + transition-property: background-color, border-color, color, box-shadow; + transition-duration: 0.2s; + transition-timing-function: ease; +} + +/* Стили для приветствия и благодарности */ +.welcome-animation, +.thank-you-animation, +.already-completed { + max-width: 800px; + margin: 50px auto; + padding: 30px; + text-align: center; + background-color: #2d2d2d; + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + transition: all 0.5s ease; +} + +.welcome-icon, +.thank-you-icon, +.completed-icon { + margin-bottom: 20px; +} + +.welcome-icon svg, +.thank-you-icon svg, +.completed-icon svg { + width: 80px; + height: 80px; + color: var(--color-primary, #2196f3); + opacity: 0.8; +} + +.welcome-title, +.thank-you-title, +.completed-title { + font-size: 2rem; + margin-bottom: 15px; + color: var(--color-primary, #2196f3); +} + +.welcome-description, +.thank-you-description, +.completed-description { + font-size: 1.1rem; + line-height: 1.6; + margin-bottom: 30px; + color: #e0e0e0; +} + +.welcome-start-btn, +.view-results-btn { + margin-top: 20px; + font-size: 1.1rem; + padding: 12px 30px; + border-radius: 30px; + animation: pulse 2s infinite; +} + +.start-again-btn { + margin-top: 20px; + margin-left: 10px; + font-size: 1.1rem; + padding: 12px 30px; + border-radius: 30px; +} + +/* Анимации для приветствия и благодарности */ +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.welcome-icon svg, +.thank-you-icon svg, +.completed-icon svg { + animation: pulse 3s infinite; +} + +/* CSS переменные для цветов */ +:root { + --color-primary: #2196f3; + --color-primary-dark: #1976d2; + --color-primary-light: #64b5f6; + --color-secondary: #757575; + --color-secondary-dark: #616161; + --color-success: #4caf50; + --color-success-dark: #388e3c; + --color-error: #f44336; + --color-error-dark: #d32f2f; + --color-muted: #9e9e9e; + --primary-rgb: 33, 150, 243; + --color-bg-dark: #1e1e1e; + --color-bg-card: #2d2d2d; + --color-bg-input: #424242; +} + +/* Стили для навигационных кнопок */ +.nav-btn { + display: flex; + align-items: center; + justify-content: center; + padding: 10px 20px; + border-radius: 30px; + transition: all 0.3s ease; +} + +.nav-btn svg { + margin: 0 5px; +} + +.nav-btn:hover { + transform: translateY(-3px); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); +} + +/* Стили для статистики в админке */ +.question-stats { + margin-bottom: 30px; + padding: 20px; + background-color: #2d2d2d; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); +} + +.question-stats h3 { + margin-top: 0; + margin-bottom: 15px; + color: #90caf9; + border-bottom: 1px solid #444; + padding-bottom: 10px; +} + +.no-stats, .no-votes { + padding: 15px; + background-color: #383838; + border-radius: 5px; + text-align: center; + color: #aaa; +} + +.stats-table { + width: 100%; + border-collapse: collapse; + margin-bottom: 15px; +} + +.stats-table th, .stats-table td { + padding: 10px; + text-align: left; + border-bottom: 1px solid #444; +} + +.stats-table th { + background-color: #383838; + color: #90caf9; +} + +.bar-container { + width: 100%; + height: 20px; + background-color: #383838; + border-radius: 3px; + overflow: hidden; +} + +.bar { + height: 100%; + background-color: #64b5f6; + border-radius: 3px; + transition: width 0.5s ease-in-out; +} + +.total-votes { + text-align: right; + font-style: italic; + color: #aaa; + margin-top: 10px; +} + +/* Стили для облака тегов в статистике */ +.tag-cloud-stats { + display: flex; + flex-wrap: wrap; + gap: 10px; + padding: 15px; + background-color: #383838; + border-radius: 5px; +} + +.tag-item { + display: inline-block; + padding: 5px 10px; + background-color: #444; + border-radius: 15px; + margin-right: 8px; + margin-bottom: 8px; + color: #90caf9; + transition: transform 0.2s ease; +} + +.tag-item:hover { + transform: scale(1.05); +} + +/* Стили для шкалы и рейтинга */ +.scale-stats { + padding: 15px; + background-color: #383838; + border-radius: 5px; +} + +.stat-item { + margin-bottom: 10px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.stat-label { + font-weight: bold; + color: #aaa; +} + +.stat-value { + font-size: 1.1em; + color: #90caf9; +} + +/* Навигация */ +.nav-header { + background-color: #212121; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3); + padding: 15px 0; + margin-bottom: 30px; +} + +.nav-container { + max-width: 1200px; + margin: 0 auto; + padding: 0 20px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.nav-logo { + font-size: 1.3rem; + font-weight: 600; + color: #61dafb; + text-decoration: none; +} + +.nav-menu { + display: flex; + gap: 20px; +} + +.nav-link { + color: #e0e0e0; + text-decoration: none; + padding: 8px 12px; + border-radius: 4px; + transition: background-color 0.2s; +} + +.nav-link:hover { + background-color: #424242; + color: #61dafb; +} + +.nav-link.active { + background-color: #424242; + color: #61dafb; +} + +/* Формы - улучшенные чекбоксы и радиокнопки */ +.form-container { + background-color: #2d2d2d; + padding: 20px; + border-radius: 5px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); + margin-bottom: 20px; +} + +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: 500; + color: #bbdefb; +} + +/* Улучшенные стили для чекбоксов и радиокнопок */ +.radio-option, +.checkbox-option { + display: flex; + align-items: center; + margin-bottom: 12px; + position: relative; +} + +.radio-option input[type="radio"], +.checkbox-option input[type="checkbox"] { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.radio-option label, +.checkbox-option label { + position: relative; + padding-left: 40px; + cursor: pointer; + display: block; + font-size: 1rem; + user-select: none; +} + +.radio-option label:before, +.checkbox-option label:before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 24px; + height: 24px; + border: 2px solid #616161; + background-color: #424242; + transition: all 0.3s; +} + +.radio-option label:before { + border-radius: 50%; +} + +.checkbox-option label:before { + border-radius: 4px; +} + +.radio-option input[type="radio"]:checked ~ label:before { + background-color: #2196f3; + border-color: #2196f3; +} + +.checkbox-option input[type="checkbox"]:checked ~ label:before { + background-color: #4caf50; + border-color: #4caf50; +} + +.radio-option label:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + background: white; + border-radius: 50%; + top: 7px; + left: 7px; + transition: all 0.2s; + opacity: 0; + transform: scale(0); +} + +.checkbox-option label:after { + content: ''; + position: absolute; + left: 9px; + top: 5px; + width: 6px; + height: 12px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg) scale(0); + opacity: 0; + transition: all 0.2s; +} + +.radio-option input[type="radio"]:checked ~ label:after { + opacity: 1; + transform: scale(1); +} + +.checkbox-option input[type="checkbox"]:checked ~ label:after { + opacity: 1; + transform: rotate(45deg) scale(1); +} + +.radio-option:hover label:before, +.checkbox-option:hover label:before { + border-color: #90caf9; +} + +/* Модальные окна с прогресс-баром */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: opacity 0.3s, visibility 0.3s; +} + +.modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.modal { + background-color: #2d2d2d; + border-radius: 5px; + box-shadow: 0 3px 15px rgba(0, 0, 0, 0.3); + width: 90%; + max-width: 500px; + padding: 20px; + position: relative; + transform: translateY(-20px); + transition: transform 0.3s; + max-height: 90vh; + overflow-y: auto; +} + +.modal-overlay.active .modal { + transform: translateY(0); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #444; +} + +.modal-header h3 { + margin: 0; + color: #64b5f6; +} + +.modal-close { + background: none; + border: none; + color: #e0e0e0; + font-size: 1.5rem; + cursor: pointer; + padding: 0; + line-height: 1; +} + +.modal-body { + margin-bottom: 20px; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 10px; + padding-top: 10px; + border-top: 1px solid #444; +} + +/* Прогресс-бар для модальных окон */ +.modal-progress { + position: absolute; + bottom: 0; + left: 0; + height: 5px; + background-color: #2196f3; + width: 0; + transition: width 2s linear; +} + +.modal-progress.active { + width: 100%; +} + +/* Анимации для опроса */ +.question-item { + opacity: 1; + transform: translateY(0); + transition: opacity 0.5s, transform 0.5s; +} + +.question-item.enter { + opacity: 0; + transform: translateY(20px); +} + +.question-item.active { + opacity: 1; + transform: translateY(0); +} + +#question-counter { + transition: opacity 0.3s, transform 0.3s; +} + +#question-counter.update { + opacity: 0; + transform: translateY(-10px); +} + +/* Анимации для благодарности и приветствия */ +.welcome-animation, +.thank-you-animation, +.already-completed { + background-color: #2d2d2d; + padding: 30px; + border-radius: 8px; + text-align: center; + margin: 50px auto; + max-width: 600px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + transition: opacity 0.5s, transform 0.5s; +} + +.welcome-icon, +.thank-you-icon, +.completed-icon { + margin-bottom: 20px; + color: #4caf50; +} + +.welcome-title, +.thank-you-title, +.completed-title { + color: #64b5f6; + margin-bottom: 15px; +} + +.welcome-description, +.thank-you-description, +.completed-description { + color: #e0e0e0; + margin-bottom: 25px; +} + +.welcome-start-btn, +.view-results-btn { + margin: 10px; +} + +/* Анимации для ошибок */ +.shake-animation { + animation: shake 0.5s; +} + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } + 20%, 40%, 60%, 80% { transform: translateX(5px); } +} + +/* Улучшенные стили для чекбоксов и радиокнопок */ +.radio-option, +.checkbox-option { + display: flex; + align-items: center; + margin-bottom: 12px; + position: relative; +} + +.radio-option input[type="radio"], +.checkbox-option input[type="checkbox"] { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.radio-option label, +.checkbox-option label { + position: relative; + padding-left: 40px; + cursor: pointer; + display: block; + font-size: 1rem; + user-select: none; +} + +.radio-option label:before, +.checkbox-option label:before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 24px; + height: 24px; + border: 2px solid #616161; + background-color: #424242; + transition: all 0.3s; +} + +.radio-option label:before { + border-radius: 50%; +} + +.checkbox-option label:before { + border-radius: 4px; +} + +.radio-option input[type="radio"]:checked ~ label:before { + background-color: #2196f3; + border-color: #2196f3; +} + +.checkbox-option input[type="checkbox"]:checked ~ label:before { + background-color: #4caf50; + border-color: #4caf50; +} + +.radio-option label:after { + content: ''; + position: absolute; + width: 10px; + height: 10px; + background: white; + border-radius: 50%; + top: 7px; + left: 7px; + transition: all 0.2s; + opacity: 0; + transform: scale(0); +} + +.checkbox-option label:after { + content: ''; + position: absolute; + left: 9px; + top: 5px; + width: 6px; + height: 12px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg) scale(0); + opacity: 0; + transition: all 0.2s; +} + +.radio-option input[type="radio"]:checked ~ label:after { + opacity: 1; + transform: scale(1); +} + +.checkbox-option input[type="checkbox"]:checked ~ label:after { + opacity: 1; + transform: rotate(45deg) scale(1); +} + +.radio-option:hover label:before, +.checkbox-option:hover label:before { + border-color: #90caf9; +} + +/* Стили для текстовых ответов в админке */ +.text-answers-list { + margin-top: 15px; +} + +.text-answer-item { + background-color: #383838; + padding: 12px 15px; + border-radius: 5px; + margin-bottom: 10px; + display: flex; + align-items: flex-start; +} + +.answer-number { + font-weight: bold; + color: #64b5f6; + margin-right: 10px; + flex-shrink: 0; +} + +.answer-text { + flex-grow: 1; + white-space: pre-wrap; + word-break: break-word; +} \ No newline at end of file diff --git a/server/routers/questioneer/public/static/js/admin.js b/server/routers/questioneer/public/static/js/admin.js new file mode 100644 index 0000000..2adf316 --- /dev/null +++ b/server/routers/questioneer/public/static/js/admin.js @@ -0,0 +1,294 @@ +/* global $, window, document, showAlert, showConfirm, showQRCodeModal */ +$(document).ready(function() { + const adminLink = window.location.pathname.split('/').pop(); + let questionnaireData = null; + + // Получаем базовый путь API (для работы и с /questioneer, и с /ms/questioneer) + const getApiPath = () => { + const pathParts = window.location.pathname.split('/'); + // Убираем последние две части пути (admin/:adminLink) + pathParts.pop(); + pathParts.pop(); + return pathParts.join('/') + '/api'; + }; + + // Загрузка данных опроса + const loadQuestionnaire = () => { + $.ajax({ + url: `${getApiPath()}/questionnaires/admin/${adminLink}`, + method: 'GET', + success: function(result) { + if (result.success) { + questionnaireData = result.data; + renderQuestionnaire(); + } else { + $('#loading').text(`Ошибка: ${result.error}`); + } + }, + error: function(error) { + console.error('Error loading questionnaire:', error); + $('#loading').text('Не удалось загрузить опрос. Пожалуйста, попробуйте позже.'); + } + }); + }; + + // Отображение данных опроса + const renderQuestionnaire = () => { + // Заполняем основные данные + $('#questionnaire-title').text(questionnaireData.title); + $('#questionnaire-description').text(questionnaireData.description || 'Нет описания'); + + // Формируем ссылки + const baseUrl = window.location.origin; + const baseQuestionnairePath = window.location.pathname.split('/admin')[0]; + const publicUrl = `${baseUrl}${baseQuestionnairePath}/poll/${questionnaireData.publicLink}`; + const adminUrl = `${baseUrl}${baseQuestionnairePath}/admin/${questionnaireData.adminLink}`; + + $('#public-link').val(publicUrl); + $('#admin-link').val(adminUrl); + + // Отображаем статистику + renderStats(questionnaireData.questions); + + // Показываем контейнер с данными + $('#loading').hide(); + $('#questionnaire-container').show(); + }; + + // Отображение статистики опроса + const renderStats = (questions) => { + const $statsContainer = $('#stats-container'); + $statsContainer.empty(); + + // Проверяем, есть ли ответы + let hasAnyResponses = false; + + // Проверяем наличие ответов для каждого типа вопросов + for (const question of questions) { + if (question.type === 'single' || question.type === 'multiple') { + if (question.options && question.options.some(option => option.votes && option.votes > 0)) { + hasAnyResponses = true; + break; + } + } else if (question.type === 'tagcloud') { + if (question.tags && question.tags.some(tag => tag.count && tag.count > 0)) { + hasAnyResponses = true; + break; + } + } else if (question.type === 'scale' || question.type === 'rating') { + if (question.responses && question.responses.length > 0) { + hasAnyResponses = true; + break; + } + } else if (question.type === 'text') { + if (question.textAnswers && question.textAnswers.length > 0) { + hasAnyResponses = true; + break; + } + } + } + + if (!hasAnyResponses) { + $statsContainer.html('
Пока нет ответов на опрос
'); + return; + } + + // Для каждого вопроса создаем блок статистики + questions.forEach((question, index) => { + const $questionStats = $('
', { class: 'question-stats' }); + const $questionTitle = $('

', { text: `${index + 1}. ${question.text}` }); + $questionStats.append($questionTitle); + + // В зависимости от типа вопроса отображаем разную статистику + if (question.type === 'single' || question.type === 'multiple') { + // Для вопросов с выбором вариантов + const totalVotes = question.options.reduce((sum, option) => sum + (option.votes || 0), 0); + + if (totalVotes === 0) { + $questionStats.append($('
', { class: 'no-votes', text: 'Нет голосов' })); + } else { + const $table = $('', { class: 'stats-table' }); + const $thead = $('').append( + $('').append( + $(''); + + question.options.forEach(option => { + const votes = option.votes || 0; + const percent = totalVotes > 0 ? Math.round((votes / totalVotes) * 100) : 0; + + const $tr = $('').append( + $('
', { text: 'Вариант' }), + $('', { text: 'Голоса' }), + $('', { text: '%' }), + $('', { text: 'Визуализация' }) + ) + ); + + const $tbody = $('
', { text: option.text }), + $('', { text: votes }), + $('', { text: `${percent}%` }), + $('').append( + $('
', { class: 'bar-container' }).append( + $('
', { + class: 'bar', + css: { width: `${percent}%` } + }) + ) + ) + ); + + $tbody.append($tr); + }); + + $table.append($thead, $tbody); + $questionStats.append($table); + $questionStats.append($('
', { class: 'total-votes', text: `Всего голосов: ${totalVotes}` })); + } + } else if (question.type === 'tagcloud') { + // Для облака тегов + if (!question.tags || question.tags.length === 0 || !question.tags.some(tag => tag.count > 0)) { + $questionStats.append($('
', { class: 'no-votes', text: 'Нет выбранных тегов' })); + } else { + const $tagCloud = $('
', { class: 'tag-cloud-stats' }); + + // Находим максимальное количество для масштабирования + const maxCount = Math.max(...question.tags.map(tag => tag.count || 0)); + + // Сортируем теги по популярности + const sortedTags = [...question.tags].sort((a, b) => (b.count || 0) - (a.count || 0)); + + sortedTags.forEach(tag => { + if (tag.count && tag.count > 0) { + const fontSize = maxCount > 0 ? 1 + (tag.count / maxCount) * 1.5 : 1; // от 1em до 2.5em + + $tagCloud.append( + $('', { + class: 'tag-item', + text: `${tag.text} (${tag.count})`, + css: { fontSize: `${fontSize}em` } + }) + ); + } + }); + + $questionStats.append($tagCloud); + } + } else if (question.type === 'scale' || question.type === 'rating') { + // Для шкалы и рейтинга + if (!question.responses || question.responses.length === 0) { + $questionStats.append($('
', { class: 'no-votes', text: 'Нет оценок' })); + } else { + const values = question.responses; + const sum = values.reduce((a, b) => a + b, 0); + const avg = sum / values.length; + const min = Math.min(...values); + const max = Math.max(...values); + + const $scaleStats = $('
', { class: 'scale-stats' }); + + $scaleStats.append( + $('
', { class: 'stat-item' }).append( + $('', { class: 'stat-label', text: 'Среднее значение:' }), + $('', { class: 'stat-value', text: avg.toFixed(1) }) + ), + $('
', { class: 'stat-item' }).append( + $('', { class: 'stat-label', text: 'Минимум:' }), + $('', { class: 'stat-value', text: min }) + ), + $('
', { class: 'stat-item' }).append( + $('', { class: 'stat-label', text: 'Максимум:' }), + $('', { class: 'stat-value', text: max }) + ), + $('
', { class: 'stat-item' }).append( + $('', { class: 'stat-label', text: 'Количество оценок:' }), + $('', { class: 'stat-value', text: values.length }) + ) + ); + + $questionStats.append($scaleStats); + } + } else if (question.type === 'text') { + // Для текстовых ответов + if (!question.textAnswers || question.textAnswers.length === 0) { + $questionStats.append($('
', { class: 'no-votes', text: 'Нет текстовых ответов' })); + } else { + const $textAnswers = $('
', { class: 'text-answers-list' }); + + question.textAnswers.forEach((answer, i) => { + $textAnswers.append( + $('
', { class: 'text-answer-item' }).append( + $('
', { class: 'answer-number', text: `#${i + 1}` }), + $('
', { class: 'answer-text', text: answer }) + ) + ); + }); + + $questionStats.append($textAnswers); + } + } + + $statsContainer.append($questionStats); + }); + }; + + // Копирование ссылок + $('#copy-public-link').on('click', function() { + $('#public-link').select(); + document.execCommand('copy'); + showAlert('Ссылка для голосования скопирована в буфер обмена', 'Копирование', null, true); + }); + + $('#copy-admin-link').on('click', function() { + $('#admin-link').select(); + document.execCommand('copy'); + showAlert('Административная ссылка скопирована в буфер обмена', 'Копирование', null, true); + }); + + // Отображение QR-кода + $('#show-qr-code').on('click', function() { + const publicUrl = $('#public-link').val(); + showQRCodeModal(publicUrl, 'QR-код для голосования'); + }); + + // Редактирование опроса + $('#edit-questionnaire').on('click', function() { + const basePath = window.location.pathname.split('/admin')[0]; + window.location.href = `${basePath}/edit/${adminLink}`; + }); + + // Удаление опроса + $('#delete-questionnaire').on('click', function() { + showConfirm('Вы уверены, что хотите удалить опрос? Все ответы будут удалены безвозвратно.', function(confirmed) { + if (confirmed) { + deleteQuestionnaire(); + } + }, 'Удаление опроса'); + }); + + // Функция удаления опроса + const deleteQuestionnaire = () => { + $.ajax({ + url: `${getApiPath()}/questionnaires/admin/${adminLink}`, + method: 'DELETE', + success: function(result) { + if (result.success) { + showAlert('Опрос успешно удален', 'Удаление опроса', function() { + window.location.href = window.location.pathname.split('/admin')[0]; + }, true); + } else { + showAlert(`Ошибка при удалении опроса: ${result.error}`, 'Ошибка'); + } + }, + error: function(error) { + console.error('Error deleting questionnaire:', error); + showAlert('Не удалось удалить опрос. Пожалуйста, попробуйте позже.', 'Ошибка'); + } + }); + }; + + // Инициализация + loadQuestionnaire(); + + // Обновление данных каждые 10 секунд + setInterval(loadQuestionnaire, 10000); +}); \ No newline at end of file diff --git a/server/routers/questioneer/public/static/js/common.js b/server/routers/questioneer/public/static/js/common.js new file mode 100644 index 0000000..7007de8 --- /dev/null +++ b/server/routers/questioneer/public/static/js/common.js @@ -0,0 +1,236 @@ +/* global $, document */ + +// Функция для создания модального окна +function createModal(options) { + // Если модальное окно уже существует, удаляем его + $('.modal-overlay').remove(); + + // Опции по умолчанию + const defaultOptions = { + title: 'Сообщение', + content: '', + closeText: 'Закрыть', + onClose: null, + showCancel: false, + cancelText: 'Отмена', + confirmText: 'Подтвердить', + onConfirm: null, + onCancel: null, + size: 'normal', // 'normal', 'large', 'small' + customClass: '', + autoClose: false, // Автоматическое закрытие по таймеру + autoCloseTime: 2000 // Время до автоматического закрытия (2 секунды) + }; + + // Объединяем пользовательские опции с опциями по умолчанию + const settings = $.extend({}, defaultOptions, options); + + // Создаем структуру модального окна + const $modalOverlay = $('
', { class: 'modal-overlay' }); + const $modal = $('
', { class: `modal ${settings.customClass}` }); + + // Устанавливаем ширину в зависимости от размера + if (settings.size === 'large') { + $modal.css('max-width', '700px'); + } else if (settings.size === 'small') { + $modal.css('max-width', '400px'); + } + + // Создаем заголовок + const $modalHeader = $('
', { class: 'modal-header' }); + const $modalTitle = $('

', { text: settings.title }); + const $modalClose = $(' +

+
+ `; + + const modal = createModal({ + title: title || 'QR-код для доступа', + content: content, + size: 'large' + }); + + // Добавляем обработчик для кнопки копирования + modal.$modal.find('.btn-copy-link').on('click', function() { + const input = modal.$modal.find('.qr-link-input'); + input.select(); + document.execCommand('copy'); + + // Показываем уведомление о копировании + const $button = $(this); + const originalText = $button.text(); + $button.text('Скопировано!'); + $button.addClass('copied'); + + setTimeout(function() { + $button.text(originalText); + $button.removeClass('copied'); + }, 1500); + }); + + return modal; +} \ No newline at end of file diff --git a/server/routers/questioneer/public/static/js/create.js b/server/routers/questioneer/public/static/js/create.js new file mode 100644 index 0000000..2597e7c --- /dev/null +++ b/server/routers/questioneer/public/static/js/create.js @@ -0,0 +1,343 @@ +/* global $, window, document, alert, showAlert, showConfirm */ +$(document).ready(function() { + const form = $('#create-questionnaire-form'); + const questionsList = $('#questions-list'); + const addQuestionBtn = $('#add-question'); + + let questionCount = 0; + + // Получаем базовый путь API (для работы и с /questioneer, и с /ms/questioneer) + const getApiPath = () => { + const pathParts = window.location.pathname.split('/'); + // Убираем последнюю часть пути (create) + pathParts.pop(); + return pathParts.join('/') + '/api'; + }; + + // Добавление нового вопроса + addQuestionBtn.on('click', function() { + addQuestion(); + }); + + // Обработка отправки формы + form.on('submit', function(e) { + e.preventDefault(); + saveQuestionnaire(); + }); + + // Делегирование событий для динамических элементов + questionsList.on('click', '.delete-question', function() { + // Удаление вопроса + const questionItem = $(this).closest('.question-item'); + showConfirm('Вы уверены, что хотите удалить этот вопрос?', function(confirmed) { + if (confirmed) { + questionItem.remove(); + renumberQuestions(); + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); + } + }); + }); + + questionsList.on('click', '.add-option', function() { + // Добавление варианта ответа + const questionIndex = $(this).data('question-index'); + addOption(questionIndex); + }); + + questionsList.on('click', '.delete-option', function() { + // Удаление варианта ответа + $(this).closest('.option-item').remove(); + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); + }); + + // Делегирование для изменения типа вопроса + questionsList.on('change', '.question-type-select', function() { + const questionItem = $(this).closest('.question-item'); + const questionIndex = questionItem.data('index'); + const optionsContainer = $(`#options-container-${questionIndex}`); + const scaleContainer = $(`#scale-container-${questionIndex}`); + + // Скрыть/показать варианты ответа в зависимости от типа вопроса + const questionType = $(this).val(); + if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) { + optionsContainer.show(); + scaleContainer.hide(); + + // Если нет вариантов, добавляем два + const optionsList = $(`#options-list-${questionIndex}`); + if (optionsList.children().length === 0) { + addOption(questionIndex); + addOption(questionIndex); + } + + // Включаем required для полей ввода вариантов + optionsList.find('input[type="text"]').prop('required', true); + } else if (questionType === 'scale') { + optionsContainer.hide(); + scaleContainer.show(); + // Отключаем required для скрытых полей + $(`#options-list-${questionIndex}`).find('input[type="text"]').prop('required', false); + } else { + optionsContainer.hide(); + scaleContainer.hide(); + // Отключаем required для скрытых полей + $(`#options-list-${questionIndex}`).find('input[type="text"]').prop('required', false); + } + + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); + }); + + // Функция для добавления нового вопроса + function addQuestion() { + const template = $('#question-template').html(); + const index = questionCount++; + + // Заменяем плейсхолдеры в шаблоне + let questionHtml = template + .replace(/\{\{index\}\}/g, index) + .replace(/\{\{number\}\}/g, index + 1); + + questionsList.append(questionHtml); + + // Показываем/скрываем контейнер вариантов в зависимости от типа вопроса + const questionType = $(`#question-type-${index}`).val(); + if (!['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) { + $(`#options-container-${index}`).hide(); + // Отключаем required для скрытых полей + $(`#options-list-${index}`).find('input[type="text"]').prop('required', false); + } else { + // Добавляем пару начальных вариантов ответа + addOption(index); + addOption(index); + } + + if (questionType === 'scale') { + $(`#scale-container-${index}`).show(); + } else { + $(`#scale-container-${index}`).hide(); + } + + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); + } + + // Функция для добавления варианта ответа + function addOption(questionIndex) { + const optionsList = $(`#options-list-${questionIndex}`); + const template = $('#option-template').html(); + + const optionIndex = optionsList.children().length; + + // Заменяем плейсхолдеры в шаблоне + let optionHtml = template + .replace(/\{\{questionIndex\}\}/g, questionIndex) + .replace(/\{\{optionIndex\}\}/g, optionIndex); + + optionsList.append(optionHtml); + + // Проверяем, видим ли контейнер опций + const optionsContainer = $(`#options-container-${questionIndex}`); + if (optionsContainer.is(':hidden')) { + // Если контейнер скрыт, отключаем required у полей ввода + optionsList.find('input[type="text"]').prop('required', false); + } + + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); + } + + // Перенумерация вопросов + function renumberQuestions() { + $('.question-item').each(function(index) { + $(this).find('h3').text(`Вопрос ${index + 1}`); + }); + } + + // Функция для обновления нумерации вопросов + function updateQuestionNumbers() { + $('.question-item').each(function(index) { + $(this).find('h3').text(`Вопрос ${index + 1}`); + }); + } + + // Сохранение опроса + function saveQuestionnaire() { + const questionnaire = { + title: $('#title').val(), + description: $('#description').val(), + displayType: 'step_by_step', // Всегда устанавливаем пошаговый режим + questions: [] + }; + + // Собираем данные о вопросах + $('.question-item').each(function() { + const index = $(this).data('index'); + const questionType = $(`#question-type-${index}`).val(); + + const question = { + text: $(`#question-text-${index}`).val(), + type: questionType, + required: $(`input[name="questions[${index}][required]"]`).is(':checked'), + options: [] + }; + + // Добавляем настройки шкалы если нужно + if (questionType === 'scale') { + question.scaleMin = parseInt($(`#scale-min-${index}`).val()) || 0; + question.scaleMax = parseInt($(`#scale-max-${index}`).val()) || 10; + question.scaleMinLabel = $(`#scale-min-label-${index}`).val() || 'Минимум'; + question.scaleMaxLabel = $(`#scale-max-label-${index}`).val() || 'Максимум'; + } + + // Собираем варианты ответа если это не текстовый вопрос или шкала + if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) { + $(`#options-list-${index} .option-item`).each(function() { + const optionText = $(this).find('input[type="text"]').val(); + + if (optionText) { + question.options.push({ + text: optionText, + count: 0 + }); + } + }); + } + + questionnaire.questions.push(question); + }); + + // Отправка на сервер + $.ajax({ + url: `${getApiPath()}/questionnaires`, + method: 'POST', + contentType: 'application/json', + data: JSON.stringify(questionnaire), + success: function(result) { + if (result.success) { + // Перенаправление на страницу администрирования опроса + const basePath = window.location.pathname.split('/create')[0]; + window.location.href = `${basePath}/admin/${result.data.adminLink}`; + } else { + showAlert(`Ошибка при создании опроса: ${result.error}`, 'Ошибка'); + } + }, + error: function(error) { + console.error('Error creating questionnaire:', error); + showAlert('Не удалось создать опрос. Пожалуйста, попробуйте позже.', 'Ошибка'); + } + }); + } + + // Функция для обновления атрибута required в зависимости от видимости полей + function updateRequiredAttributes() { + // Для полей вопросов + $('.question-item').each(function() { + const questionType = $(this).find('.question-type-select').val(); + const textInput = $(this).find('.question-text'); + const optionsContainer = $(this).find('.options-container'); + + // Обновляем required для текстового поля вопроса + if (textInput.is(':visible')) { + textInput.prop('required', true); + } else { + textInput.prop('required', false); + } + + // Обновляем required для полей опций + if (questionType === 'single_choice' || questionType === 'multiple_choice') { + optionsContainer.find('input[type="text"]').each(function() { + if ($(this).is(':visible')) { + $(this).prop('required', true); + } else { + $(this).prop('required', false); + } + }); + } else { + optionsContainer.find('input[type="text"]').prop('required', false); + } + + // Для шкалы оценки + if (questionType === 'scale') { + const minInput = $(this).find('.scale-min'); + const maxInput = $(this).find('.scale-max'); + const minLabelInput = $(this).find('.scale-min-label'); + const maxLabelInput = $(this).find('.scale-max-label'); + + if (minInput.is(':visible')) minInput.prop('required', true); + else minInput.prop('required', false); + + if (maxInput.is(':visible')) maxInput.prop('required', true); + else maxInput.prop('required', false); + + if (minLabelInput.is(':visible')) minLabelInput.prop('required', true); + else minLabelInput.prop('required', false); + + if (maxLabelInput.is(':visible')) maxLabelInput.prop('required', true); + else maxLabelInput.prop('required', false); + } + }); + + // Для основных полей формы + const titleInput = $('#title'); + const descriptionInput = $('#description'); + + if (titleInput.is(':visible')) titleInput.prop('required', true); + else titleInput.prop('required', false); + + if (descriptionInput.is(':visible')) descriptionInput.prop('required', false); // Описание не обязательно + } + + // Инициализация с одним вопросом + addQuestion(); + + // Обработчик отправки формы + $('#create-questionnaire-form').on('submit', function(e) { + // Обновляем атрибуты required перед отправкой + updateRequiredAttributes(); + + // Проверяем валидность формы + if (!this.checkValidity()) { + e.preventDefault(); + e.stopPropagation(); + + // Находим первый невалидный элемент и прокручиваем к нему + const firstInvalid = $(this).find(':invalid').first(); + if (firstInvalid.length) { + $('html, body').animate({ + scrollTop: firstInvalid.offset().top - 100 + }, 500); + + // Добавляем класс ошибки к родительскому элементу вопроса + firstInvalid.closest('.question-item').addClass('error'); + setTimeout(() => { + firstInvalid.closest('.question-item').removeClass('error'); + }, 3000); + } + } + + $(this).addClass('was-validated'); + }); + + // Инициализируем атрибуты required + updateRequiredAttributes(); +}); + +// Обработчик удаления вопроса +$(document).on('click', '.remove-question', function() { + $(this).closest('.question-item').remove(); + updateQuestionNumbers(); + + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); +}); + +// Обработчик удаления опции +$(document).on('click', '.remove-option', function() { + $(this).closest('.option-item').remove(); + + // Вызываем функцию обновления атрибутов required + updateRequiredAttributes(); +}); \ No newline at end of file diff --git a/server/routers/questioneer/public/static/js/edit.js b/server/routers/questioneer/public/static/js/edit.js new file mode 100644 index 0000000..90120f6 --- /dev/null +++ b/server/routers/questioneer/public/static/js/edit.js @@ -0,0 +1,332 @@ +/* global $, window, document, showAlert, showConfirm, showQRCodeModal */ +$(document).ready(function() { + const form = $('#edit-questionnaire-form'); + const questionsList = $('#questions-list'); + const addQuestionBtn = $('#add-question'); + const adminLink = window.location.pathname.split('/').pop(); + + let questionCount = 0; + let questionnaireData = null; + + // Получаем базовый путь API + const getApiPath = () => { + const pathParts = window.location.pathname.split('/'); + // Убираем последние две части пути (edit/:adminLink) + pathParts.pop(); + pathParts.pop(); + return pathParts.join('/') + '/api'; + }; + + // Загрузка данных опроса + const loadQuestionnaire = () => { + $.ajax({ + url: `${getApiPath()}/questionnaires/admin/${adminLink}`, + method: 'GET', + success: function(result) { + if (result.success) { + questionnaireData = result.data; + fillFormData(); + $('#loading').hide(); + $('#edit-form-container').show(); + } else { + $('#loading').text(`Ошибка: ${result.error}`); + } + }, + error: function(error) { + console.error('Error loading questionnaire:', error); + $('#loading').text('Не удалось загрузить опрос. Пожалуйста, попробуйте позже.'); + } + }); + }; + + // Заполнение формы данными опроса + const fillFormData = () => { + // Заполняем основные данные + $('#title').val(questionnaireData.title); + $('#description').val(questionnaireData.description || ''); + $('#display-type').val(questionnaireData.displayType); + + // Формируем ссылки + const baseUrl = window.location.origin; + const baseQuestionnairePath = window.location.pathname.split('/edit')[0]; + const publicUrl = `${baseUrl}${baseQuestionnairePath}/poll/${questionnaireData.publicLink}`; + const adminUrl = `${baseUrl}${baseQuestionnairePath}/admin/${questionnaireData.adminLink}`; + + $('#public-link').val(publicUrl); + $('#admin-link').val(adminUrl); + + // Добавляем вопросы + questionsList.empty(); + + if (questionnaireData.questions && questionnaireData.questions.length > 0) { + questionnaireData.questions.forEach((question, index) => { + addQuestion(question); + }); + } else { + // Если нет вопросов, добавляем пустой + addQuestion(); + } + + renumberQuestions(); + }; + + // Добавление нового вопроса + addQuestionBtn.on('click', function() { + addQuestion(); + renumberQuestions(); + }); + + // Обработка отправки формы + form.on('submit', function(e) { + e.preventDefault(); + saveQuestionnaire(); + }); + + // Делегирование событий для динамических элементов + questionsList.on('click', '.delete-question', function() { + // Удаление вопроса + const questionItem = $(this).closest('.question-item'); + questionItem.remove(); + renumberQuestions(); + }); + + questionsList.on('click', '.add-option', function() { + // Добавление варианта ответа + const questionIndex = $(this).data('question-index'); + addOption(questionIndex); + }); + + questionsList.on('click', '.delete-option', function() { + // Удаление варианта ответа + $(this).closest('.option-item').remove(); + }); + + // Делегирование для изменения типа вопроса + questionsList.on('change', '.question-type-select', function() { + const questionItem = $(this).closest('.question-item'); + const questionIndex = questionItem.data('index'); + const optionsContainer = $(`#options-container-${questionIndex}`); + const scaleContainer = $(`#scale-container-${questionIndex}`); + + // Показываем/скрываем контейнеры в зависимости от типа вопроса + const questionType = $(this).val(); + if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) { + optionsContainer.show(); + scaleContainer.hide(); + + // Если нет вариантов, добавляем два + const optionsList = $(`#options-list-${questionIndex}`); + if (optionsList.children().length === 0) { + addOption(questionIndex); + addOption(questionIndex); + } + + // Включаем required для полей ввода вариантов + optionsList.find('input[type="text"]').prop('required', true); + } else if (questionType === 'scale') { + optionsContainer.hide(); + scaleContainer.show(); + // Отключаем required для скрытых полей + $(`#options-list-${questionIndex}`).find('input[type="text"]').prop('required', false); + } else { + optionsContainer.hide(); + scaleContainer.hide(); + // Отключаем required для скрытых полей + $(`#options-list-${questionIndex}`).find('input[type="text"]').prop('required', false); + } + }); + + // Копирование ссылок + $('#copy-public-link').on('click', function() { + $('#public-link').select(); + document.execCommand('copy'); + showAlert('Ссылка для голосования скопирована в буфер обмена', 'Копирование'); + }); + + $('#copy-admin-link').on('click', function() { + $('#admin-link').select(); + document.execCommand('copy'); + showAlert('Административная ссылка скопирована в буфер обмена', 'Копирование'); + }); + + // Отображение QR-кода + $('#show-qr-code').on('click', function() { + const publicUrl = $('#public-link').val(); + showQRCodeModal(publicUrl, 'QR-код для голосования'); + }); + + // Возврат к админке + $('#back-to-admin').on('click', function(e) { + e.preventDefault(); + const basePath = window.location.pathname.split('/edit')[0]; + window.location.href = `${basePath}/admin/${adminLink}`; + }); + + // Функция для добавления нового вопроса + function addQuestion(questionData) { + const template = $('#question-template').html(); + const index = questionCount++; + + // Заменяем плейсхолдеры в шаблоне + let questionHtml = template + .replace(/\{\{index\}\}/g, index) + .replace(/\{\{number\}\}/g, index + 1); + + questionsList.append(questionHtml); + + // Если есть данные вопроса - заполняем поля + if (questionData) { + $(`#question-text-${index}`).val(questionData.text); + $(`#question-type-${index}`).val(questionData.type); + + if (questionData.required) { + $(`input[name="questions[${index}][required]"]`).prop('checked', true); + } + + // Добавляем варианты ответа если они есть + if (questionData.options && questionData.options.length > 0) { + questionData.options.forEach(option => { + addOption(index, option.text); + }); + } + + // Заполняем настройки шкалы если нужно + if (questionData.scaleMax) { + $(`#scale-max-${index}`).val(questionData.scaleMax); + } + } + + // Показываем/скрываем контейнеры в зависимости от типа вопроса + const questionType = $(`#question-type-${index}`).val(); + if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) { + $(`#options-container-${index}`).show(); + $(`#scale-container-${index}`).hide(); + + // Если нет вариантов и не загружены данные, добавляем два + if (!questionData && $(`#options-list-${index}`).children().length === 0) { + addOption(index); + addOption(index); + } + } else if (questionType === 'scale') { + $(`#options-container-${index}`).hide(); + $(`#scale-container-${index}`).show(); + } else { + $(`#options-container-${index}`).hide(); + $(`#scale-container-${index}`).hide(); + } + } + + // Функция для добавления варианта ответа + function addOption(questionIndex, optionText) { + const optionsList = $(`#options-list-${questionIndex}`); + const template = $('#option-template').html(); + + const optionIndex = optionsList.children().length; + + // Заменяем плейсхолдеры в шаблоне + let optionHtml = template + .replace(/\{\{questionIndex\}\}/g, questionIndex) + .replace(/\{\{optionIndex\}\}/g, optionIndex); + + optionsList.append(optionHtml); + + // Если есть текст варианта - устанавливаем его + if (optionText) { + optionsList.children().last().find('input[type="text"]').val(optionText); + } + + // Проверяем, видим ли контейнер опций + const optionsContainer = $(`#options-container-${questionIndex}`); + if (optionsContainer.is(':hidden')) { + // Если контейнер скрыт, отключаем required у полей ввода + optionsList.find('input[type="text"]').prop('required', false); + } + } + + // Перенумерация вопросов + function renumberQuestions() { + $('.question-item').each(function(index) { + $(this).find('h3').text(`Вопрос ${index + 1}`); + }); + } + + // Сохранение опроса + function saveQuestionnaire() { + const questionnaire = { + title: $('#title').val(), + description: $('#description').val(), + displayType: $('#display-type').val(), + questions: [] + }; + + // Собираем данные о вопросах + $('.question-item').each(function() { + const index = $(this).data('index'); + const questionType = $(`#question-type-${index}`).val(); + + const question = { + text: $(`#question-text-${index}`).val(), + type: questionType, + required: $(`input[name="questions[${index}][required]"]`).is(':checked'), + options: [] + }; + + // Добавляем настройки шкалы если нужно + if (questionType === 'scale') { + question.scaleMax = parseInt($(`#scale-max-${index}`).val()); + } + + // Собираем варианты ответа если это не текстовый вопрос или оценка + if (['single_choice', 'multiple_choice', 'tag_cloud'].includes(questionType)) { + $(`#options-list-${index} .option-item`).each(function() { + const optionText = $(this).find('input[type="text"]').val(); + + if (optionText) { + // Сохраняем количество голосов из старых данных + let count = 0; + const optionIndex = $(this).data('index'); + + if (questionnaireData && + questionnaireData.questions[index] && + questionnaireData.questions[index].options && + questionnaireData.questions[index].options[optionIndex]) { + count = questionnaireData.questions[index].options[optionIndex].count || 0; + } + + question.options.push({ + text: optionText, + count: count + }); + } + }); + } + + questionnaire.questions.push(question); + }); + + // Отправка на сервер + $.ajax({ + url: `${getApiPath()}/questionnaires/${adminLink}`, + method: 'PUT', + contentType: 'application/json', + data: JSON.stringify(questionnaire), + success: function(result) { + if (result.success) { + showAlert('Опрос успешно обновлен', 'Успешно', function() { + const basePath = window.location.pathname.split('/edit')[0]; + window.location.href = `${basePath}/admin/${adminLink}`; + }); + } else { + showAlert(`Ошибка при обновлении опроса: ${result.error}`, 'Ошибка'); + } + }, + error: function(error) { + console.error('Error updating questionnaire:', error); + showAlert('Не удалось обновить опрос. Пожалуйста, попробуйте позже.', 'Ошибка'); + } + }); + } + + // Инициализация + loadQuestionnaire(); +}); \ No newline at end of file diff --git a/server/routers/questioneer/public/static/js/index.js b/server/routers/questioneer/public/static/js/index.js new file mode 100644 index 0000000..68f970d --- /dev/null +++ b/server/routers/questioneer/public/static/js/index.js @@ -0,0 +1,67 @@ +/* global $, window, document */ +$(document).ready(function() { + // Функция для получения базового пути API + const getApiPath = () => { + // Извлекаем базовый путь из URL страницы + const pathParts = window.location.pathname.split('/'); + // Если последний сегмент пустой (из-за /) - удаляем его + if (pathParts[pathParts.length - 1] === '') { + pathParts.pop(); + } + + // Путь до корня приложения + return pathParts.join('/') + '/api'; + }; + + // Функция для загрузки списка опросов + const loadQuestionnaires = () => { + $.ajax({ + url: getApiPath() + '/questionnaires', + method: 'GET', + success: function(result) { + if (result.success) { + renderQuestionnaires(result.data); + } else { + $('#questionnaires-container').html(`

Ошибка: ${result.error}

`); + } + }, + error: function(error) { + console.error('Error loading questionnaires:', error); + $('#questionnaires-container').html('

Не удалось загрузить опросы. Пожалуйста, попробуйте позже.

'); + } + }); + }; + + // Функция для отображения списка опросов + const renderQuestionnaires = (questionnaires) => { + if (!questionnaires || questionnaires.length === 0) { + $('#questionnaires-container').html('

У вас еще нет созданных опросов.

'); + return; + } + + // Получаем базовый путь (для работы и с /questioneer, и с /ms/questioneer) + const basePath = window.location.pathname.endsWith('/') + ? window.location.pathname + : window.location.pathname + '/'; + + const questionnairesHTML = questionnaires.map(q => ` +
+

${q.title}

+

${q.description || 'Нет описания'}

+

Создан: ${new Date(q.createdAt).toLocaleString()}

+ +
+ `).join(''); + + $('#questionnaires-container').html(questionnairesHTML); + }; + + // Инициализация страницы + loadQuestionnaires(); + + // Обновление данных каждые 30 секунд + setInterval(loadQuestionnaires, 30000); +}); \ No newline at end of file diff --git a/server/routers/questioneer/public/static/js/poll.js b/server/routers/questioneer/public/static/js/poll.js new file mode 100644 index 0000000..d7e78f1 --- /dev/null +++ b/server/routers/questioneer/public/static/js/poll.js @@ -0,0 +1,1069 @@ +/* global $, window, document, showAlert */ +$(document).ready(function() { + const publicLink = window.location.pathname.split('/').pop(); + let questionnaireData = null; + + // Элементы DOM + const loadingEl = $('#loading'); + const containerEl = $('#questionnaire-container'); + const titleEl = $('#questionnaire-title'); + const descriptionEl = $('#questionnaire-description'); + const questionsContainerEl = $('#questions-container'); + const formEl = $('#poll-form'); + const resultsContainerEl = $('#results-container'); + const pollResultsContainerEl = $('#poll-results-container'); + + // Элементы навигации для пошаговых опросов + const navigationControlsEl = $('#navigation-controls'); + const prevButtonEl = $('#prev-question'); + const nextButtonEl = $('#next-question'); + const questionCounterEl = $('#question-counter'); + const submitButtonEl = $('#submit-button'); + + // Для пошаговых опросов + let currentQuestionIndex = 0; + + // Проверка доступности localStorage + const isLocalStorageAvailable = () => { + try { + const testKey = 'test'; + window.localStorage.setItem(testKey, testKey); + window.localStorage.removeItem(testKey); + return true; + } catch (e) { + return false; + } + }; + + // Ключ для localStorage + const getLocalStorageKey = () => `questionnaire_${publicLink}_completed`; + + // Проверка на повторное прохождение опроса + const checkIfAlreadyCompleted = () => { + if (!isLocalStorageAvailable()) return false; + return window.localStorage.getItem(getLocalStorageKey()) === 'true'; + }; + + // Сохранение информации о прохождении опроса + const markAsCompleted = () => { + if (!isLocalStorageAvailable()) return; + window.localStorage.setItem(getLocalStorageKey(), 'true'); + }; + + // Получаем базовый путь API + const getApiPath = () => { + const pathParts = window.location.pathname.split('/'); + // Убираем последние две части пути (poll/:publicLink) + pathParts.pop(); + pathParts.pop(); + return pathParts.join('/') + '/api'; + }; + + // Загрузка данных опроса + const loadQuestionnaire = () => { + $.ajax({ + url: `${getApiPath()}/questionnaires/public/${publicLink}`, + method: 'GET', + success: function(result) { + if (result.success) { + questionnaireData = result.data; + + // Проверяем, проходил ли пользователь уже этот опрос + if (checkIfAlreadyCompleted()) { + showAlreadyCompletedMessage(); + } else { + showWelcomeAnimation(); + } + } else { + loadingEl.text(`Ошибка: ${result.error}`); + } + }, + error: function(error) { + console.error('Error loading questionnaire:', error); + loadingEl.text('Не удалось загрузить опрос. Пожалуйста, попробуйте позже.'); + } + }); + }; + + // Показываем анимацию приветствия + const showWelcomeAnimation = () => { + // Скрываем индикатор загрузки + loadingEl.hide(); + + // Создаем элемент приветствия + const $welcomeEl = $('
', { + id: 'welcome-animation', + class: 'welcome-animation', + css: { + opacity: 0, + transform: 'translateY(20px)' + } + }); + + const $welcomeIcon = $('
', { + class: 'welcome-icon', + html: '' + }); + + const $welcomeTitle = $('

', { + text: `Добро пожаловать в опрос "${questionnaireData.title}"`, + class: 'welcome-title' + }); + + const $welcomeDescription = $('

', { + text: questionnaireData.description || 'Пожалуйста, ответьте на следующие вопросы:', + class: 'welcome-description' + }); + + const $startButton = $(' + Вопрос 1 из ${questionnaireData.questions.length} + +

+ `); + + // Обработчики для навигации + $('#prev-question').on('click', prevQuestion); + $('#next-question').on('click', nextQuestion); + + // Показываем только первый вопрос + currentQuestionIndex = 0; + renderCurrentQuestion(); + + // Показываем контейнер с данными + containerEl.show(); + }; + + // Отображение текущего вопроса в пошаговом режиме + const renderCurrentQuestion = () => { + // Очищаем контейнер вопросов + questionsContainerEl.empty(); + + // Получаем текущий вопрос + const currentQuestion = questionnaireData.questions[currentQuestionIndex]; + + // Отображаем вопрос + renderQuestion(questionsContainerEl, currentQuestion, currentQuestionIndex); + + // Добавляем классы для анимации + const $currentQuestionEl = questionsContainerEl.find(`.question-item[data-index="${currentQuestionIndex}"]`); + $currentQuestionEl.addClass('enter'); + + // Запускаем анимацию появления после небольшой задержки + setTimeout(() => { + $currentQuestionEl.removeClass('enter').addClass('active'); + }, 50); + + // Обновляем счетчик вопросов + questionCounterEl.addClass('update'); + questionCounterEl.text(`Вопрос ${currentQuestionIndex + 1} из ${questionnaireData.questions.length}`); + + // Снимаем класс анимации счетчика + setTimeout(() => { + questionCounterEl.removeClass('update'); + }, 500); + + // Управляем состоянием кнопок навигации + prevButtonEl.prop('disabled', currentQuestionIndex === 0); + nextButtonEl.text( + currentQuestionIndex === questionnaireData.questions.length - 1 + ? 'Отправить' + : 'Далее' + ); + }; + + // Переход к предыдущему вопросу + const prevQuestion = function() { + if (currentQuestionIndex > 0) { + currentQuestionIndex--; + renderCurrentQuestion(); + } + }; + + // Переход к следующему вопросу или завершение опроса + const nextQuestion = function() { + const currentQuestion = questionnaireData.questions[currentQuestionIndex]; + + // Проверяем, что на текущий вопрос есть ответ, если он обязательный + if (currentQuestion.required && !checkQuestionAnswer(currentQuestion, currentQuestionIndex)) { + // Добавляем класс ошибки к текущему вопросу + const $currentQuestionEl = questionsContainerEl.find(`.question-item[data-index="${currentQuestionIndex}"]`); + $currentQuestionEl.addClass('error shake'); + + // Удаляем класс ошибки после окончания анимации + setTimeout(() => { + $currentQuestionEl.removeClass('shake'); + }, 500); + + return; + } + + // Если есть еще вопросы, переходим к следующему + if (currentQuestionIndex < questionnaireData.questions.length - 1) { + // Анимируем текущий вопрос перед переходом к следующему + questionsContainerEl.find(`.question-item[data-index="${currentQuestionIndex}"]`).addClass('exit'); + + // Увеличиваем индекс текущего вопроса + currentQuestionIndex++; + + // Рендерим новый вопрос после небольшой задержки + setTimeout(() => { + renderCurrentQuestion(); + }, 200); + } else { + // Анимируем контейнер перед отправкой формы + questionsContainerEl.find('.question-item').addClass('exit'); + + // Последний вопрос, отправляем форму + setTimeout(() => { + // Отключаем атрибут required у невидимых полей перед отправкой + $('input[required]:not(:visible), textarea[required]:not(:visible)').prop('required', false); + + submitForm(); + }, 300); + } + }; + + // Проверка наличия ответа на вопрос + const checkQuestionAnswer = (question, questionIndex) => { + switch (question.type) { + case 'single_choice': + return $(`.question-item[data-index="${questionIndex}"] input[name="question_${questionIndex}"]:checked`).length > 0; + + case 'multiple_choice': + return $(`.question-item[data-index="${questionIndex}"] input[type="checkbox"]:checked:not(.multiple-choice-validation)`).length > 0; + + case 'text': + return $(`.question-item[data-index="${questionIndex}"] textarea`).val().trim() !== ''; + + case 'scale': + return $(`.question-item[data-index="${questionIndex}"] input[name="question_${questionIndex}"]:checked`).length > 0; + + case 'tag_cloud': + return $(`.question-item[data-index="${questionIndex}"] .tag-item.selected`).length > 0; + + default: + return true; + } + }; + + // Отображение всех вопросов (стандартный режим) + const renderQuestions = () => { + questionsContainerEl.empty(); + + $.each(questionnaireData.questions, function(questionIndex, question) { + renderQuestion(questionsContainerEl, question, questionIndex); + }); + }; + + // Отображение одного вопроса + const renderQuestion = (container, question, questionIndex) => { + const $questionEl = $('
', { + class: 'question-item', + 'data-index': questionIndex + }); + + const $questionTitle = $('

', { + text: question.text, + class: 'question-title' + }); + + if (question.required) { + const $requiredMark = $('', { + class: 'required-mark', + text: ' *' + }); + $questionTitle.append($requiredMark); + } + + $questionEl.append($questionTitle); + + // Отображение вариантов ответа в зависимости от типа вопроса + switch (question.type) { + case 'single_choice': + renderSingleChoice($questionEl, question, questionIndex); + break; + case 'multiple_choice': + renderMultipleChoice($questionEl, question, questionIndex); + break; + case 'text': + renderTextAnswer($questionEl, question, questionIndex); + break; + case 'scale': + renderScale($questionEl, question, questionIndex); + break; + case 'rating': // Обратная совместимость для рейтинга + renderScale($questionEl, { ...question, scaleMax: 5 }, questionIndex); + break; + case 'tag_cloud': + renderTagCloud($questionEl, question, questionIndex); + break; + } + + container.append($questionEl); + }; + + // Отображение вопроса с одиночным выбором + const renderSingleChoice = ($container, question, questionIndex) => { + const $optionsContainer = $('
', { + class: 'radio-options-container' + }); + + $.each(question.options, function(optionIndex, option) { + const $optionContainer = $('
', { + class: 'radio-option' + }); + + const $optionInput = $('', { + type: 'radio', + id: `question_${questionIndex}_option_${optionIndex}`, + name: `question_${questionIndex}`, + value: optionIndex, + required: question.required + }); + + const $optionLabel = $('