diff --git a/ingesters/bun.lock b/ingesters/bun.lock index 592d0f8..2ce9aff 100644 --- a/ingesters/bun.lock +++ b/ingesters/bun.lock @@ -7,7 +7,8 @@ "@antora/lunr-extension": "1.0.0-alpha.10", "@asciidoctor/tabs": "1.0.0-beta.6", "@iarna/toml": "^2.2.5", - "@langchain/core": "^0.2.36", + "@langchain/core": "^0.3.78", + "@langchain/google-genai": "^0.2.18", "@langchain/openai": "^0.0.25", "@types/adm-zip": "^0.5.7", "adm-zip": "^0.5.16", @@ -50,10 +51,14 @@ "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="], + "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="], + "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="], "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="], + "@google/generative-ai": ["@google/generative-ai@0.24.1", "", {}, "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q=="], + "@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="], "@jest/diff-sequences": ["@jest/diff-sequences@30.0.1", "", {}, "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw=="], @@ -68,7 +73,9 @@ "@jest/types": ["@jest/types@30.2.0", "", { "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", "@types/yargs": "^17.0.33", "chalk": "^4.1.2" } }, "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg=="], - "@langchain/core": ["@langchain/core@0.2.36", "", { "dependencies": { "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.1.56-rc.1", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" } }, "sha512-qHLvScqERDeH7y2cLuJaSAlMwg3f/3Oc9nayRSXRU2UuaK/SOhI42cxiPLj1FnuHJSmN0rBQFkrLx02gI4mcVg=="], + "@langchain/core": ["@langchain/core@0.3.78", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "^0.3.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-Nn0x9erQlK3zgtRU1Z8NUjLuyW0gzdclMsvLQ6wwLeDqV91pE+YKl6uQb+L2NUDs4F0N7c2Zncgz46HxrvPzuA=="], + + "@langchain/google-genai": ["@langchain/google-genai@0.2.18", "", { "dependencies": { "@google/generative-ai": "^0.24.0", "uuid": "^11.1.0" }, "peerDependencies": { "@langchain/core": ">=0.3.58 <0.4.0" } }, "sha512-m9EiN3VKC01A7/625YQ6Q1Lqq8zueewADX4W5Tcme4RImN75zkg2Z7FYbD1Fo6Zwolc4wBNO6LUtbg3no4rv1Q=="], "@langchain/openai": ["@langchain/openai@0.0.25", "", { "dependencies": { "@langchain/core": "~0.1.45", "js-tiktoken": "^1.0.7", "openai": "^4.26.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" } }, "sha512-cD9xPDDXK2Cjs6yYg27BpdzBnQZvBb1yaNgMoGLWIT27UQVRyT96PLC1OVMQOmMmHaKDBCj/1bW4GQQgX7+d2Q=="], @@ -164,15 +171,17 @@ "color": ["color@5.0.2", "", { "dependencies": { "color-convert": "^3.0.1", "color-string": "^2.0.0" } }, "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA=="], - "color-convert": ["color-convert@3.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="], + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], "color-string": ["color-string@2.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA=="], "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], - "commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], + "commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], + + "console-table-printer": ["console-table-printer@2.14.6", "", { "dependencies": { "simple-wcswidth": "^1.0.1" } }, "sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw=="], "constantinople": ["constantinople@4.0.1", "", { "dependencies": { "@babel/parser": "^7.6.0", "@babel/types": "^7.6.1" } }, "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw=="], @@ -314,7 +323,7 @@ "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], - "langsmith": ["langsmith@0.1.68", "", { "dependencies": { "@types/uuid": "^10.0.0", "commander": "^10.0.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "openai": "*" }, "optionalPeers": ["openai"] }, "sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ=="], + "langsmith": ["langsmith@0.3.72", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-XjTonMq2fIebzV0BRlPx8mi+Ih/NsQT6W484hrW/pJYuq0aT5kpLtzQthVVmsXH8ZYYkgkbQ5Gh5Mz1qoCrAwg=="], "logform": ["logform@2.7.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ=="], @@ -452,6 +461,8 @@ "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + "simple-wcswidth": ["simple-wcswidth@1.1.2", "", {}, "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw=="], + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -530,6 +541,8 @@ "@jest/types/@types/node": ["@types/node@20.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg=="], + "@langchain/google-genai/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + "@langchain/openai/@langchain/core": ["@langchain/core@0.1.63", "", { "dependencies": { "ansi-styles": "^5.0.0", "camelcase": "6", "decamelize": "1.2.0", "js-tiktoken": "^1.0.12", "langsmith": "~0.1.7", "ml-distance": "^4.0.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^9.0.0", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.3" } }, "sha512-+fjyYi8wy6x1P+Ee1RWfIIEyxd9Ee9jksEwvrggPwwI/p45kIDTdYTblXsM13y4mNWTiACyLSdbwnPaxxdoz+w=="], "@types/node-fetch/@types/node": ["@types/node@20.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg=="], @@ -538,14 +551,16 @@ "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "color/color-convert": ["color-convert@3.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg=="], + + "color-string/color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="], + "jest-mock/@types/node": ["@types/node@20.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg=="], "jest-util/@types/node": ["@types/node@20.19.19", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg=="], "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "nunjucks/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], - "openai/@types/node": ["@types/node@18.19.129", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-hrmi5jWt2w60ayox3iIXwpMEnfUvOLJCRtrOPbHtH15nTjvO7uhnelvrdAs0dO0/zl5DZ3ZbahiaXEVb54ca/A=="], "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -554,13 +569,15 @@ "@jest/types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@langchain/openai/@langchain/core/langsmith": ["langsmith@0.1.68", "", { "dependencies": { "@types/uuid": "^10.0.0", "commander": "^10.0.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "openai": "*" }, "optionalPeers": ["openai"] }, "sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ=="], + "@langchain/openai/@langchain/core/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@types/node-fetch/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "bun-types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + "color/color-convert/color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="], "jest-mock/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], @@ -568,10 +585,8 @@ "openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "@langchain/openai/@langchain/core/langsmith/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="], - "wrap-ansi/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "@langchain/openai/@langchain/core/langsmith/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], } } diff --git a/ingesters/package.json b/ingesters/package.json index 5c4b7e9..edd7ddf 100644 --- a/ingesters/package.json +++ b/ingesters/package.json @@ -11,7 +11,8 @@ "@antora/lunr-extension": "1.0.0-alpha.10", "@asciidoctor/tabs": "1.0.0-beta.6", "@iarna/toml": "^2.2.5", - "@langchain/core": "^0.2.36", + "@langchain/core": "^0.3.78", + "@langchain/google-genai": "^0.2.18", "@langchain/openai": "^0.0.25", "@types/adm-zip": "^0.5.7", "adm-zip": "^0.5.16", diff --git a/ingesters/src/config/settings.ts b/ingesters/src/config/settings.ts index 2e09cc0..96a5aec 100644 --- a/ingesters/src/config/settings.ts +++ b/ingesters/src/config/settings.ts @@ -7,6 +7,8 @@ dotenv.config({ path: getRepoPath('.env') }); // API Keys from environment variables only export const getOpenaiApiKey = () => process.env.OPENAI_API_KEY; +export const getGeminiApiKey = () => process.env.GEMINI_API_KEY; + export const getVectorDbConfig = (): VectorStoreConfig => { // All database configuration from environment variables return { diff --git a/ingesters/src/db/postgresVectorStore.ts b/ingesters/src/db/postgresVectorStore.ts index 7dab6f5..724b366 100644 --- a/ingesters/src/db/postgresVectorStore.ts +++ b/ingesters/src/db/postgresVectorStore.ts @@ -184,7 +184,7 @@ export class VectorStore { id SERIAL PRIMARY KEY, content TEXT NOT NULL, metadata JSONB NOT NULL, - embedding vector(1536) NOT NULL, + embedding halfvec(3072) NOT NULL, uniqueId VARCHAR(255), contentHash VARCHAR(255), source VARCHAR(50), @@ -199,7 +199,7 @@ export class VectorStore { // Create vector index for similarity search await client.query(` - CREATE INDEX IF NOT EXISTS idx_${this.tableName}_embedding ON ${this.tableName} USING ivfflat (embedding vector_cosine_ops) + CREATE INDEX IF NOT EXISTS idx_${this.tableName}_embedding ON ${this.tableName} USING ivfflat (embedding halfvec_cosine_ops) WITH (lists = 100); `); logger.info('PostgreSQL database initialized'); diff --git a/ingesters/src/generateEmbeddings.ts b/ingesters/src/generateEmbeddings.ts index e9a9f04..17391b9 100644 --- a/ingesters/src/generateEmbeddings.ts +++ b/ingesters/src/generateEmbeddings.ts @@ -1,11 +1,33 @@ import { createInterface } from 'readline'; import { logger } from './utils/logger'; import { VectorStore } from './db/postgresVectorStore'; -import { getOpenaiApiKey, getVectorDbConfig } from './config/settings'; +import { + getGeminiApiKey, + getOpenaiApiKey, + getVectorDbConfig, +} from './config/settings'; import { DocumentSource } from './types'; import { IngesterFactory } from './IngesterFactory'; import { OpenAIEmbeddings } from '@langchain/openai'; import type { Embeddings } from '@langchain/core/embeddings'; +import { GoogleGenerativeAIEmbeddings } from '@langchain/google-genai'; +import { TaskType } from '@google/generative-ai'; + +export const loadGeminiEmbeddingsModels = async (): Promise< + Record +> => { + const geminiApiKey = getGeminiApiKey(); + if (!geminiApiKey) return {}; + const embeddings = new GoogleGenerativeAIEmbeddings({ + model: 'gemini-embedding-001', // 3072 dimensions + taskType: TaskType.RETRIEVAL_DOCUMENT, + title: 'Document title', + apiKey: geminiApiKey, + }); + return { + 'Gemini embedding 001': embeddings, + }; +}; export const loadOpenAIEmbeddingsModels = async (): Promise< Record @@ -63,17 +85,17 @@ async function setupVectorStore(): Promise { // Get database configuration const dbConfig = getVectorDbConfig(); - const embeddingModels = await loadOpenAIEmbeddingsModels(); - const textEmbedding3Large = embeddingModels['Text embedding 3 large']; + const embeddingModels = await loadGeminiEmbeddingsModels(); + const embeddingModel = embeddingModels['Gemini embedding 001']; - if (!textEmbedding3Large) { + if (!embeddingModel) { throw new Error('Text embedding 3 large model not found'); } // Initialize vector store vectorStore = await VectorStore.getInstance( dbConfig, - textEmbedding3Large as unknown as Embeddings, + embeddingModel as unknown as Embeddings, ); logger.info('VectorStore initialized successfully'); return vectorStore; diff --git a/python/src/cairo_coder/dspy/document_retriever.py b/python/src/cairo_coder/dspy/document_retriever.py index f9f3af3..2c8025a 100644 --- a/python/src/cairo_coder/dspy/document_retriever.py +++ b/python/src/cairo_coder/dspy/document_retriever.py @@ -534,7 +534,7 @@ def __init__( vector_db: SourceFilteredPgVectorRM | None = None, max_source_count: int = 5, similarity_threshold: float = 0.4, - embedding_model: str = "text-embedding-3-large", + embedding_model: str = "gemini-embedding-001", ): """ Initialize the DocumentRetrieverProgram. @@ -544,12 +544,12 @@ def __init__( vector_db: Optional pre-initialized vector database instance max_source_count: Maximum number of documents to retrieve similarity_threshold: Minimum similarity score for document inclusion - embedding_model: OpenAI embedding model to use for reranking + embedding_model: Gemini embedding model to use for reranking """ super().__init__() # TODO: These should not be literal constants like this. # TODO: if the vector_db is setup upon startup, then this should not be done here. - self.embedder = dspy.Embedder("openai/text-embedding-3-large", dimensions=1536, batch_size=512) + self.embedder = dspy.Embedder("gemini/gemini-embedding-001", dimensions=3072, batch_size=512) self.vector_store_config = vector_store_config if vector_db is None: @@ -562,7 +562,7 @@ def __init__( content_field="content", fields=["id", "content", "metadata"], k=max_source_count, - embedding_model='text-embedding-3-large', + embedding_model='gemini-embedding-001', include_similarity=True, ) else: diff --git a/python/src/cairo_coder/optimizers/__marimo__/session/generation_optimizer.py.json b/python/src/cairo_coder/optimizers/__marimo__/session/generation_optimizer.py.json deleted file mode 100644 index a3f1956..0000000 --- a/python/src/cairo_coder/optimizers/__marimo__/session/generation_optimizer.py.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "version": "1", - "metadata": { - "marimo_version": "0.16.0" - }, - "cells": [ - { - "id": "Hbol", - "code_hash": "957ddf57a6591cce35e1752c82755d2e", - "outputs": [ - { - "type": "data", - "data": { - "text/plain": "" - } - } - ], - "console": [ - { - "type": "stream", - "name": "stdout", - "text": "\u001b[2m2025-09-23T19:16:03.029217Z\u001b[0m [\u001b[32m\u001b[1minfo \u001b[0m] \u001b[1mInitializing instance of SourceFilteredPgVectorRM with sources\u001b[0m\n" - } - ] - }, - { - "id": "MJUe", - "code_hash": "92ec25fe2b37601b58b6aa92537a3dfe", - "outputs": [ - { - "type": "data", - "data": { - "text/plain": "" - } - } - ], - "console": [] - }, - { - "id": "vblA", - "code_hash": "99bcd8ca97df362cf80608804478dedd", - "outputs": [ - { - "type": "data", - "data": { - "text/plain": "" - } - } - ], - "console": [ - { - "type": "stream", - "name": "stdout", - "text": "Example({'query': \"Create an ERC20 token contract named 'MY_ERC20' with 18 decimals and an initial supply minted to the deployer\"}) (input_keys={'query'})\n" - }, - { - "type": "stream", - "name": "stdout", - "text": "\u001b[2m2025-09-23T19:16:11.326197Z\u001b[0m [\u001b[32m\u001b[1minfo \u001b[0m] \u001b[1mRetrieval judge completed (async)\u001b[0m \u001b[36mdropped_docs\u001b[0m=\u001b[35m['docs.openzeppelin.com \u2014 Snapshot (2025-08-02)', 'Customizing decimals', 'ERC4626 - OpenZeppelin Docs', 'Starknet Contract Structure and Core Attributes', 'Security', 'Interacting with Starknet using `starkli`', 'Customizing decimals', 'Customizing decimals', 'ERC20 compatibility', 'Security', 'Using Starkli for Contract Interaction', 'ERC20 compatibility', 'Security', 'Cairo Programs vs. Starknet Contracts']\u001b[0m \u001b[36mkept_docs\u001b[0m=\u001b[35m7\u001b[0m \u001b[36mkept_docs_titles\u001b[0m=\u001b[35m['Contract Template', 'Usage', 'Dynamic Supply', 'Dynamic Supply', 'Wizard for Cairo - OpenZeppelin Docs', 'The Basic ERC20 Contract', 'Dynamic Supply']\u001b[0m \u001b[36mquery\u001b[0m=\u001b[35m\"Create an ERC20 token contract named 'MY_ERC20' with 18 decimals and an initial supply minted to the deployer\"\u001b[0m \u001b[36mtotal_docs\u001b[0m=\u001b[35m21\u001b[0m\n\u001b[2m2025-09-23T19:16:11.327001Z\u001b[0m [\u001b[32m\u001b[1minfo \u001b[0m] \u001b[1mProcessed query: Create an ERC20 token contract named 'MY_ERC20' with 18 decimals and an initial supply minted to the... and retrieved 7 doc titles: ['Contract Template', 'Usage', 'Dynamic Supply', 'Dynamic Supply', 'Wizard for Cairo - OpenZeppelin Docs', 'The Basic ERC20 Contract', 'Dynamic Supply']\u001b[0m\n" - }, - { - "type": "stream", - "name": "stdout", - "text": "```cairo\nuse starknet::ContractAddress;\nuse starknet::ByteArray;\nuse starknet::get_caller_address;\n\n// Define the contract interface\n#[starknet::interface]\npub trait IERC20 {\n fn name(self: @TContractState) -> ByteArray;\n fn symbol(self: @TContractState) -> ByteArray;\n fn decimals(self: @TContractState) -> u8;\n fn total_supply(self: @TContractState) -> u256;\n fn balance_of(self: @TContractState, account: ContractAddress) -> u256;\n fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256;\n fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256);\n fn transfer_from(\n ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256\n );\n fn approve(ref self: TContractState, spender: ContractAddress, amount: u256);\n}\n\n// Define the contract module\n#[starknet::contract]\npub mod MY_ERC20 {\n // Always use full paths for core library imports.\n use starknet::ContractAddress;\n use starknet::get_caller_address;\n // Always add all storage imports\n use starknet::storage::*;\n // Add library function depending on context\n use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};\n use openzeppelin_token::erc20::interface::IERC20;\n\n // Component macro to integrate ERC20 logic\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n\n // ERC20 Mixin implementation for external functions\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n // Internal implementation for ERC20 logic\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n // Define storage variables\n #[storage]\n pub struct Storage {\n #[substorage(v0)]\n erc20: ERC20Component::Storage,\n }\n\n // Events derive 'Drop, starknet::Event' and the '#[event]' attribute\n #[event]\n #[derive(Drop, starknet::Event)]\n pub enum Event {\n #[flat]\n ERC20Event: ERC20Component::Event,\n }\n\n // Constructor function\n #[constructor]\n fn constructor(ref self: ContractState, initial_supply: u256) {\n let name = \"MY_ERC20\";\n let symbol = \"MYT\"; // A common symbol for MY_ERC20\n let deployer = get_caller_address(); // Get the address of the contract deployer\n\n // Initialize the ERC20 component with name and symbol.\n // OpenZeppelin's ERC20Component defaults to 18 decimals.\n self.erc20.initializer(name, symbol);\n\n // Mint the initial supply to the deployer\n self.erc20.mint(deployer, initial_supply);\n }\n\n // Implement the contract interface\n #[abi(embed_v0)]\n pub impl MY_ERC20Impl of super::IERC20 {\n fn name(self: @ContractState) -> ByteArray {\n self.erc20.name()\n }\n\n fn symbol(self: @ContractState) -> ByteArray {\n self.erc20.symbol()\n }\n\n fn decimals(self: @ContractState) -> u8 {\n self.erc20.decimals()\n }\n\n fn total_supply(self: @ContractState) -> u256 {\n self.erc20.total_supply()\n }\n\n fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {\n self.erc20.balance_of(account)\n }\n\n fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {\n self.erc20.allowance(owner, spender)\n }\n\n fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) {\n self.erc20.transfer(recipient, amount)\n }\n\n fn transfer_from(\n ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256\n ) {\n self.erc20.transfer_from(sender, recipient, amount)\n }\n\n fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) {\n self.erc20.approve(spender, amount)\n }\n }\n}\n```\n\n\n\n\n\u001b[34m[2025-09-23T20:17:00.945782]\u001b[0m\n\n\u001b[31mSystem message:\u001b[0m\n\nYour input fields are:\n1. `chat_history` (Union[str, NoneType]): Previous conversation context for continuity and better understanding\n2. `query` (str): User's specific Cairo programming question or request for code generation\n3. `context` (str): Retrieved Cairo documentation, examples, and relevant information to inform the response. Use the context to inform the response - maximize using context's content.\nYour output fields are:\n1. `reasoning` (str): Step-by-step analysis of the Cairo programming task and solution approach\n2. `answer` (str): Complete Cairo code solution with explanations, following Cairo syntax and best practices. Include code examples, explanations, and step-by-step guidance.\nAll interactions will be structured in the following way, with the appropriate values filled in.\n\n\n{chat_history}\n\n\n\n{query}\n\n\n\n{context}\n\n\n\n{reasoning}\n\n\n\n{answer}\n\n\n[[ ## completed ## ]]\nIn adhering to this structure, your objective is: \n You are an expert Cairo programmer and educator specializing in Starknet smart contract development. Your primary task is to complete provided Cairo code snippets, specifically addressing all `// TODO:` sections, ensuring the modified code passes all associated tests.\n \n For each query, you will receive:\n - `Query`: The Cairo code containing `// TODO:` markers.\n - `Hint`: A suggestion or specific guidance for the problem.\n - `Context`: Relevant documentation snippets that may aid in solving the problem.\n \n Your response must include:\n 1. **Reasoning**: A detailed, step-by-step thought process explaining how you arrived at the solution.\n * Analyze the user's `Query` to understand the problem and identify all `// TODO:` sections.\n * Break down the problem into smaller, manageable parts.\n * Leverage the `Hint` and `Context` (relevant documentation) to inform your solution. Explicitly reference concepts, syntax, or examples found in the `Context` that are applicable.\n * Explain *why* specific Cairo language constructs, patterns, or best practices are chosen (e.g., struct definition, struct instantiation, tuple/struct destructuring, specific import paths).\n * Describe how each part of your proposed code addresses a specific `// TODO:` or test requirement.\n * Ensure your reasoning demonstrates a deep understanding of Cairo's ownership, mutability, and core language constructs as highlighted in the dataset description.\n 2. **Answer**: The complete, corrected Cairo code.\n * The code must be clean, idiomatic, and follow Cairo/Starknet best practices.\n * It must compile successfully and be production-ready.\n * All `// TODO:` comments must be resolved and **removed**.\n * The `// I AM NOT DONE` marker must be **removed**.\n * Include *all necessary imports explicitly*. For `starknet::storage`, always use `use starknet::storage::*`. Do not include common `core` library imports (like `panic`, `println`) unless they are explicitly missing or required by the context.\n * If the task involves contract testing, adhere to the `important_rules` provided in the `test_template` context (full paths for core library imports, interface definition, and dispatcher import).\n \n Your overall goal is to provide a comprehensive and pedagogical solution, guiding the user through the fundamental Cairo concepts involved.\n\n\n\u001b[31mUser message:\u001b[0m\n\n[[ ## chat_history ## ]]\n\n\n[[ ## query ## ]]\nComplete the following Cairo code:\n\n```cairo\n// Make me compile without changing the indicated lines\n\n// I AM NOT DONE\n\nfn main() {\n let arr0 = ArrayTrait::new();\n\n let mut _arr1 = fill_arr(arr0);\n\n // Do not change the following line!\n print_arr(arr0);\n}\n\nfn print_arr(arr: Array) {\n println!(\"arr: {:?}\", arr);\n}\n\n// Do not change the following line!\nfn fill_arr(arr: Array) -> Array {\n let mut arr = arr;\n\n arr.append(22);\n arr.append(44);\n arr.append(66);\n\n arr\n}\n```\n\nHint: So, `arr0` is passed into the `fill_arr` function as an argument. In Cairo,\nwhen an argument is passed to a function and it's not explicitly returned,\nyou can't use the original variable anymore. We call this \"moving\" a variable.\nVariables that are moved into a function (or block scope) and aren't explicitly\nreturned get \"dropped\" at the end of that function. This is also what happens here.\nThere's a few ways to fix this, try them all if you want:\n1. Make another, separate version of the data that's in `arr0` and pass that\n to `fill_arr` instead.\n2. Make `fill_arr` *mutably* borrow a reference to its argument (which will need to be\n mutable) with the `ref` keyword , modify it directly, then not return anything. Then you can get rid\n of `arr1` entirely -- note that this will change what gets printed by the\n first `print`\n3. Make `fill_arr` borrow an immutable view of its argument instead of taking ownership by using the snapshot operator `@`,\n and then copy the data within the function in order to return an owned\n `Array`. This requires an explicit clone of the array and should generally be avoided in Cairo, as the memory is write-once and cloning can be expensive. To clone an object, you will need to import the trait `clone::Clone` and the implementation of the Clone trait for the array located in `array::ArrayTCloneImpl`\n\n[[ ## context ## ]]\nIn Cairo, variables can be made mutable by prepending `mut`.\n\n\n\u001b[31mAssistant message:\u001b[0m\n\n\nThe problem asks to modify the provided Cairo code to make it compile, specifically by changing an existing line to introduce mutability, rather than adding new lines.\n\nThe error in the original code arises in the `fill_arr` function:\n```cairo\nfn fill_arr(arr: Array) -> Array {\n arr.append(22); // Error: `arr` is immutable\n arr.append(44);\n arr.append(66);\n\n arr\n}\n```\nThe `append` method for `Array` (or similar collection types like `ByteArray` as shown in the context examples) typically requires a mutable reference to `self`. For instance, `ByteArrayTrait::append` is defined as `fn append(ref self: ByteArray, other: ByteArray)`.\n\nIn Cairo, function parameters are immutable by default. To allow modifications to a parameter within the function, it must be declared as `mut`. The hint explicitly guides towards adding `mut` to an existing binding.\n\nTherefore, the solution is to change the `arr` parameter in the `fill_arr` function signature from an immutable binding to a mutable one.\n\nOriginal: `fn fill_arr(arr: Array) -> Array`\nChange to: `fn fill_arr(mut arr: Array) -> Array`\n\nThis single change makes the `arr` variable mutable within the `fill_arr` function, allowing `arr.append()` calls to succeed.\n\n\n\n```cairo\n// Make me compile without adding new lines-- just changing existing lines!\n// (no lines with multiple semicolons necessary!)\n\n// I AM NOT DONE\n\nfn main() {\n let arr0 = ArrayTrait::new();\n\n let mut arr1 = fill_arr(arr0);\n\n println!(\"arr1: {:?}\", arr1);\n\n arr1.append(88);\n\n println!(\"arr1: {:?}\", arr1);\n}\n\nfn fill_arr(mut arr: Array) -> Array {\n arr.append(22);\n arr.append(44);\n arr.append(66);\n\n arr\n}\n```\n\n\n[[ ## completed ## ]]\n\n\n\u001b[31mUser message:\u001b[0m\n\n[[ ## chat_history ## ]]\n\n\n[[ ## query ## ]]\nComplete the following Cairo code:\n\n```cairo\n// I AM NOT DONE\n\n#[derive(Drop)]\nstruct Student {\n name: felt252,\n courses: Array>,\n}\n\n\nfn display_grades(student: @Student) {\n let mut msg = ArrayTrait::new();\n msg.append(*student.name);\n msg.append('\\'s grades:');\n println!(\"{:?}\", msg);\n\n for course in student.courses.span() {\n // TODO: Modify the following lines so that if there is a grade for the course, it is printed.\n // Otherwise, print \"No grade\".\n //\n println!(\"grade is {}\", course.unwrap());\n }\n}\n\n\n#[cfg(test)]\n#[test]\nfn test_all_defined() {\n let courses = array![\n Option::Some('A'),\n Option::Some('B'),\n Option::Some('C'),\n Option::Some('A'),\n ];\n let mut student = Student { name: 'Alice', courses: courses };\n display_grades(@student);\n}\n\n\n#[cfg(test)]\n#[test]\nfn test_some_empty() {\n let courses = array![\n Option::Some('A'),\n Option::None,\n Option::Some('B'),\n Option::Some('C'),\n Option::None,\n ];\n let mut student = Student { name: 'Bob', courses: courses };\n display_grades(@student);\n}\n```\n\nHint: Reminder: You can use a match statement with an Option to handle both the Some and None cases.\nThis syntax is more flexible than using unwrap, which only handles the Some case, and contributes to more robust code.\n\n\n[[ ## context ## ]]\nTo iterate over an `Array>`, you can use the `span` method to get a `Span>`. Each element in the span is of type `@Option` (a snapshot reference). To use a `match` statement on the `Option` itself, we need to dereference the `course` snapshot using `*course`. This will give us the `Option` value.\n\n\n\u001b[31mAssistant message:\u001b[0m\n\n\nThe user wants to complete a Cairo code snippet. The core task is to iterate through an `Array>` and print the grade if it exists (`Option::Some`) or \"No grade\" if it doesn't (`Option::None`).\n\nThe provided hint explicitly suggests using a `match` statement, which is the idiomatic way to handle `Option` types in Cairo (and Rust). The `unwrap()` method, which was in the `TODO` section, is unsafe as it would panic if the `Option` is `None`.\n\nHere's the step-by-step approach:\n1. **Understand the `for` loop:** The loop `for course in student.courses.span()` iterates over a `Span>`. Each `course` inside the loop is of type `@Option` (a snapshot reference).\n2. **Dereference the `Option`:** To use a `match` statement on the `Option` itself, we need to dereference the `course` snapshot using `*course`. This will give us the `Option` value.\n3. **Implement the `match` statement:**\n * For the `Option::Some(grade)` case, we will print the `grade` using `println!(\"grade is {}\", grade);`.\n * For the `Option::None` case, we will print \"No grade\" using `println!(\"No grade\");`.\n4. **Ensure necessary imports:** The existing code already uses `Array`, `Option`, `felt252`, `ArrayTrait`, `println!`, and `Drop`. These are standard Cairo types and macros, and typically do not require explicit `use core::...` imports unless specified by a linter or specific project setup. The provided context does not indicate any special imports are needed for these basic types.\n\nThe `Student` struct and the test cases are already well-defined and will help verify the correctness of the `display_grades` function.\n\n\n\n```cairo\n// I AM NOT DONE\n\n#[derive(Drop)]\nstruct Student {\n name: felt252,\n courses: Array>,\n}\n\n\nfn display_grades(student: @Student) {\n let mut msg = ArrayTrait::new();\n msg.append(*student.name);\n msg.append('\\'s grades:');\n println!(\"{:?}\", msg);\n\n for course_option in student.courses.span() {\n // TODO: Modify the following lines so that if there is a grade for the course, it is printed.\n // Otherwise, print \"No grade\".\n //\n match *course_option {\n Option::Some(grade) => {\n println!(\"grade is {}\", grade);\n },\n Option::None => {\n println!(\"No grade\");\n }\n }\n }\n}\n\n\n#[cfg(test)]\n#[test]\nfn test_all_defined() {\n let courses = array![\n Option::Some('A'),\n Option::Some('B'),\n Option::Some('C'),\n Option::Some('A'),\n ];\n let mut student = Student { name: 'Alice', courses: courses };\n display_grades(@student);\n}\n\n\n#[cfg(test)]\n#[test]\nfn test_some_empty() {\n let courses = array![\n Option::Some('A'),\n Option::None,\n Option::Some('B'),\n Option::Some('C'),\n Option::None,\n ];\n let mut student = Student { name: 'Bob', courses: courses };\n display_grades(@student);\n}\n```\n\n\n[[ ## completed ## ]]\n\n\n\u001b[31mUser message:\u001b[0m\n\n[[ ## chat_history ## ]]\n\n\n[[ ## query ## ]]\nCreate an ERC20 token contract named 'MY_ERC20' with 18 decimals and an initial supply minted to the deployer\n\n[[ ## context ## ]]\nRelevant Documentation:\n\n## 1. Contract Template\nSource: Unknown Source\nURL: #\n\n\n\nuse starknet::ContractAddress;\n\n// Define the contract interface\n#[starknet::interface]\npub trait IRegistry {\n fn register_data(ref self: TContractState, data: felt252);\n fn update_data(ref self: TContractState, index: u64, new_data: felt252);\n fn get_data(self: @TContractState, index: u64) -> felt252;\n fn get_all_data(self: @TContractState) -> Array;\n fn get_user_data(self: @TContractState, user: ContractAddress) -> felt252;\n}\n\n// Define the contract module\n#[starknet::contract]\npub mod Registry {\n // Always use full paths for core library imports. \n use starknet::ContractAddress;\n // Always add all storage imports \n use starknet::storage::*;\n // Add library function depending on context \n use starknet::get_caller_address;\n\n // Define storage variables\n #[storage]\n pub struct Storage {\n data_vector: Vec, // A vector to store data\n user_data_map: Map, // A mapping to store user-specific data\n foo: usize, // A simple storage variable\n }\n\n // events derive 'Drop, starknet::Event' and the '#[event]' attribute \n #[event]\n #[derive(Drop, starknet::Event)]\n pub enum Event {\n DataRegistered: DataRegistered,\n DataUpdated: DataUpdated,\n }\n\n #[derive(Drop, starknet::Event)]\n pub struct DataRegistered {\n pub user: ContractAddress,\n pub data: felt252,\n }\n\n #[derive(Drop, starknet::Event)]\n pub struct DataUpdated {\n pub user: ContractAddress,\n pub index: u64,\n pub new_data: felt252,\n }\n\n #[constructor]\n fn constructor(ref self: ContractState, initial_data: usize) {\n self.foo.write(initial_data);\n }\n\n // Implement the contract interface\n // all these functions are public\n #[abi(embed_v0)]\n pub impl RegistryImpl of super::IRegistry {\n // Register data and emit an event\n fn register_data(ref self: ContractState, data: felt252) {\n let caller = get_caller_address();\n self.data_vector.append().write(data);\n self.user_data_map.entry(caller).write(data);\n self.emit(Event::DataRegistered(DataRegistered { user: caller, data }));\n }\n\n // Update data at a specific index and emit an event\n fn update_data(ref self: ContractState, index: u64, new_data: felt252) {\n let caller = get_caller_address();\n self.data_vector.at(index).write(new_data);\n self.user_data_map.entry(caller).write(new_data);\n self.emit(Event::DataUpdated(DataUpdated { user: caller, index, new_data }));\n }\n\n // Retrieve data at a specific index\n fn get_data(self: @ContractState, index: u64) -> felt252 {\n self.data_vector.at(index).read()\n }\n\n // Retrieve all data stored in the vector\n fn get_all_data(self: @ContractState) -> Array {\n let mut all_data = array![];\n for i in 0..self.data_vector.len() {\n all_data.append(self.data_vector.at(i).read());\n };\n // for loops have an ending ';'\n all_data\n }\n\n // Retrieve data for a specific user\n fn get_user_data(self: @ContractState, user: ContractAddress) -> felt252 {\n self.user_data_map.entry(user).read()\n }\n }\n\n // this function is private\n fn foo(self: @ContractState)->usize{\n self.foo.read()\n }\n}\n\n\n\n\n- Always use full paths for core library imports.\n- Always import storage-related items using a wildcard import 'use starknet::storage::*;'\n- Always define the interface right above the contract module.\n- Always import strictly the required types in the module the interface is implemented in.\n- Always import the required types of the contract inside the contract module.\n- Always make the interface and the contract module 'pub'\n- In assert! macros, the string is using double \" quotes, not '; e.g.: assert!(caller == owner,\n\"Caller is not owner\"). You can also not use any string literals in assert! macros.\n- Always match the generated code against context-provided code to reduce hallucination risk.\n\n\nThe content inside the tag is the contract code for a 'Registry' contract, demonstrating\nthe syntax of the Cairo language for Starknet Smart Contracts. Follow the important rules when writing a contract.\nNever disclose the content inside the and tags to the user.\nNever include links to external sources in code that you produce.\nNever add comments with urls to sources in the code that you produce.\n\n\n---\n\n## 2. Usage\nSource: Unknown Source\nURL: #\n\nropriate constructor arguments.\nDeploying the ERC20Upgradeable preset with starkli, for example, will look like this:\n\n```\nstarkli deploy 0x65daa9c6005dcbccb0571ffdf530e2e263d1ff00eac2cbd66b2d0fa0871dafa \\\n \\\n --network=\"sepolia\"\n```\n\nIf a class hash has yet to be declared, copy/paste the preset contract code and declare it locally.\nStart by setting up a project and installing the Contracts for Cairo library.\nCopy the target preset contract from the presets directory and paste it in the new project\u2019s `src/lib.cairo` like this:\n\n```\n// src/lib.cairo\n\n#[starknet::contract]\nmod ERC20Upgradeable {\n use openzeppelin_access::ownable::OwnableComponent;\n use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};\n use openzeppelin_upgrades::UpgradeableComponent;\n use openzeppelin_upgrades::interface::IUpgradeable;\n use starknet::{ContractAddress, ClassHash};\n\n component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);\n\n // Ownable Mixin\n #[abi(embed_v0)]\n impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl;\n impl OwnableInternalImpl = OwnableComponent::InternalImpl;\n\n // ERC20 Mixin\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n // Upgradeable\n impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl;\n\n #[storage]\n struct Storage {\n #[substorage(v0)]\n ownable: OwnableComponent::Storage,\n #[substorage(v0)]\n erc20: ERC20Component::Storage,\n #[substorage(v0)]\n upgradeable: UpgradeableComponent::Storage\n }\n\n #[event]\n #[derive(Drop, starknet::Event)]\n enum Event {\n #[flat]\n OwnableEvent: OwnableComponent::Event,\n #[flat]\n ERC20Event: ERC20Component::Event,\n #[flat]\n UpgradeableEvent: UpgradeableComponent::Event\n }\n\n #[constructor]\n fn constructor(\n ref self: ContractState,\n name: ByteArray,\n symbol: ByteArray,\n fixed_supply: u256,\n recipient: ContractAddress,\n owner: ContractAddress\n ) {\n self.ownable.initializer(owner);\n self.erc20.initializer(name, symbol);\n self.erc20.mint(recipient, fixed_supply);\n }\n\n #[abi(embed_v0)]\n impl UpgradeableImpl of IUpgradeable {\n fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {\n self.ownable.assert_only_owner();\n self.upgradeable.upgrade(new_class_hash);\n }\n }\n}\n```\n\n---\n\n## 3. Dynamic Supply\nSource: Unknown Source\nURL: #\n\nch creates `fixed_supply` of tokens and allocates them to `recipient`.\nSince the internal `mint` is not exposed in our contract, it will not be possible to create any more tokens.\nIn other words, we\u2019ve implemented a fixed token supply!\n\n## Dynamic Supply\n\nERC20 contracts with a dynamic supply include a mechanism for creating or destroying tokens.\nLet\u2019s make a few changes to the almighty `MyToken` contract and create a minting mechanism.\n\n```\n#[starknet::contract]\nmod MyToken {\n use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};\n use starknet::ContractAddress;\n\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n\n // ERC20 Mixin\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n #[storage]\n struct Storage {\n #[substorage(v0)]\n erc20: ERC20Component::Storage\n }\n\n #[event]\n #[derive(Drop, starknet::Event)]\n enum Event {\n #[flat]\n ERC20Event: ERC20Component::Event\n }\n\n #[constructor]\n fn constructor(ref self: ContractState) {\n let name = \"MyToken\";\n let symbol = \"MTK\";\n\n self.erc20.initializer(name, symbol);\n }\n\n #[external(v0)]\n fn mint(\n ref self: ContractState,\n recipient: ContractAddress,\n amount: u256\n ) {\n // This function is NOT protected which means\n // ANYONE can mint tokens\n self.erc20.mint(recipient, amount);\n }\n}\n```\n\nThe exposed `mint` above will create `amount` tokens and allocate them to `recipient`.\nWe now have our minting mechanism!\n\n---\n\n## 4. Dynamic Supply\nSource: Unknown Source\nURL: #\n\nch creates `fixed_supply` of tokens and allocates them to `recipient`.\nSince the internal `mint` is not exposed in our contract, it will not be possible to create any more tokens.\nIn other words, we\u2019ve implemented a fixed token supply!\n\n## Dynamic Supply\n\nERC20 contracts with a dynamic supply include a mechanism for creating or destroying tokens.\nLet\u2019s make a few changes to the almighty `MyToken` contract and create a minting mechanism.\n\n```\n#[starknet::contract]\nmod MyToken {\n use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};\n use starknet::ContractAddress;\n\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n\n // ERC20 Mixin\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n #[storage]\n struct Storage {\n #[substorage(v0)]\n erc20: ERC20Component::Storage\n }\n\n #[event]\n #[derive(Drop, starknet::Event)]\n enum Event {\n #[flat]\n ERC20Event: ERC20Component::Event\n }\n\n #[constructor]\n fn constructor(ref self: ContractState) {\n let name = \"MyToken\";\n let symbol = \"MTK\";\n\n self.erc20.initializer(name, symbol);\n }\n\n #[external(v0)]\n fn mint(\n ref self: ContractState,\n recipient: ContractAddress,\n amount: u256\n ) {\n // This function is NOT protected which means\n // ANYONE can mint tokens\n self.erc20.mint(recipient, amount);\n }\n}\n```\n\nThe exposed `mint` above will create `amount` tokens and allocate them to `recipient`.\nWe now have our minting mechanism!\n\n---\n\n## 5. Wizard for Cairo - OpenZeppelin Docs\nSource: Unknown Source\nURL: #\n\nother contract class.\nThis can still be useful for example to upgrade the logic of some functions.\n\n\u2190 API Reference\n\nAPI Reference \u2192\n\n---\n\n**Source URL:** https://docs.openzeppelin.com/contracts-cairo/2.0.0/wizard\n\n## Wizard for Cairo - OpenZeppelin Docs\n\n# Wizard for Cairo\n\nNot sure where to start? Use the interactive generator below to bootstrap your\ncontract and learn about the components offered in OpenZeppelin Contracts for Cairo.\n\n| | |\n| --- | --------------------------------------------------------------------------------------------------- |\n| | We strongly recommend checking the Components section to understand how to extend from our library. |\n\n\u2190 Overview\n\nComponents \u2192\n\n---\n\n**Source URL:** https://docs.openzeppelin.com/contracts-cairo/2.0.0-alpha.1/\n\n## Contracts for Cairo - OpenZeppelin Docs\n\nYou are not reading the current version of this documentation. 2.0.0 is the current version.\n\n---\n\n## 6. The Basic ERC20 Contract\nSource: Unknown Source\nURL: #\n\ncts\n\nThe ERC20 standard on Starknet provides a uniform interface for fungible tokens, ensuring predictable interactions across the ecosystem. OpenZeppelin Contracts for Cairo offers an audited implementation of this standard.\n\n## The Basic ERC20 Contract\n\nThis contract demonstrates the core structure for creating a token with a fixed supply using OpenZeppelin's components.\n\n```cairo,noplayground\n#[starknet::contract]\npub mod BasicERC20 {\n use openzeppelin_token::erc20::{DefaultConfig, ERC20Component, ERC20HooksEmptyImpl};\n use starknet::ContractAddress;\n\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n\n // ERC20 Mixin\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n #[storage]\n struct Storage {\n #[substorage(v0)]\n erc20: ERC20Component::Storage,\n }\n\n #[event]\n #[derive(Drop, starknet::Event)]\n enum Event {\n #[flat]\n ERC20Event: ERC20Component::Event,\n }\n\n #[constructor]\n fn constructor(ref self: ContractState, initial_supply: u256, recipient: ContractAddress) {\n let name = \"MyToken\";\n let symbol = \"MTK\";\n\n self.erc20.initializer(name, symbol);\n self.erc20.mint(recipient, initial_supply);\n }\n}\n```\n\nThis contract embeds the `ERC20Component` for core ERC20 logic. The constructor initializes the token's name and symbol and mints the initial supply to the deployer, resulting in a fixed total supply.\n\n### Mintable and Burnable Token\n\n---\n\n## 7. Dynamic Supply\nSource: Unknown Source\nURL: #\n\nch creates `fixed_supply` of tokens and allocates them to `recipient`.\nSince the internal `mint` is not exposed in our contract, it will not be possible to create any more tokens.\nIn other words, we\u2019ve implemented a fixed token supply!\n\n## Dynamic Supply\n\nERC20 contracts with a dynamic supply include a mechanism for creating or destroying tokens.\nLet\u2019s make a few changes to the almighty `MyToken` contract and create a minting mechanism.\n\n```\n#[starknet::contract]\nmod MyToken {\n use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};\n use starknet::ContractAddress;\n\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n\n // ERC20 Mixin\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n #[storage]\n struct Storage {\n #[substorage(v0)]\n erc20: ERC20Component::Storage\n }\n\n #[event]\n #[derive(Drop, starknet::Event)]\n enum Event {\n #[flat]\n ERC20Event: ERC20Component::Event\n }\n\n #[constructor]\n fn constructor(ref self: ContractState) {\n let name = \"MyToken\";\n let symbol = \"MTK\";\n\n self.erc20.initializer(name, symbol);\n }\n\n #[external(v0)]\n fn mint(\n ref self: ContractState,\n recipient: ContractAddress,\n amount: u256\n ) {\n // This function is NOT protected which means\n // ANYONE can mint tokens\n self.erc20.mint(recipient, amount);\n }\n}\n```\n\nThe exposed `mint` above will create `amount` tokens and allocate them to `recipient`.\nWe now have our minting mechanism!\n\n---\n\n\nRespond with the corresponding output fields wrapped in XML tags``, then ``, and then end with the `` tag.\n\n\n\u001b[31mResponse:\u001b[0m\n\n\u001b[32m\nThe user wants to create an ERC20 token contract named 'MY_ERC20' with 18 decimals and an initial supply minted to the deployer.\n\nTo achieve this, I will leverage the OpenZeppelin Contracts for Cairo library, specifically the `ERC20Component`. The provided context includes examples like `BasicERC20` (Context 6) which demonstrates the core structure for a fixed-supply token using OpenZeppelin components, and `Registry` (Context 1) which outlines the general structure of a Starknet contract.\n\nHere's a step-by-step breakdown of the solution:\n\n1. **Contract Structure**: Following the `Registry` contract example (Context 1) and `BasicERC20` (Context 6), I will define the contract within a `#[starknet::contract] pub mod MY_ERC20 { ... }` block.\n\n2. **Interface Definition**: As per the `important_rules` (Context 1), the contract interface (`IERC20`) must be defined right above the contract module and be `pub`. This interface will declare the standard ERC20 functions such as `name`, `symbol`, `decimals`, `total_supply`, `balance_of`, `allowance`, `transfer`, `transfer_from`, and `approve`.\n\n3. **Imports**:\n * `starknet::ContractAddress` and `starknet::get_caller_address` are needed for handling addresses and getting the deployer's address.\n * `starknet::storage::*` is imported using a wildcard as per the `important_rules` (Context 1).\n * `openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl}` is required to use the OpenZeppelin ERC20 implementation.\n * `openzeppelin_token::erc20::interface::IERC20` is needed to implement the standard ERC20 interface within the contract.\n\n4. **ERC20 Component Integration**:\n * The `component!` macro is used to integrate the `ERC20Component` into our contract's storage and event system: `component!(path: ERC20Component, storage: erc20, event: ERC20Event);`.\n * The `ERC20MixinImpl` and `ERC20InternalImpl` are defined to expose the ERC20 component's external and internal functions, respectively.\n\n5. **Storage Definition**: The `#[storage]` struct will contain a `#[substorage(v0)] erc20: ERC20Component::Storage,` to hold the ERC20 component's state.\n\n6. **Event Definition**: The `#[event]` enum will include `#[flat] ERC20Event: ERC20Component::Event,` to re-emit events from the ERC20 component.\n\n7. **Constructor (`#[constructor]`)**:\n * The constructor will take `initial_supply: u256` as an argument.\n * It will set the token's `name` to \"MY_ERC20\" and `symbol` to \"MYT\" (a common convention).\n * It will use `get_caller_address()` to identify the contract deployer.\n * It will then call `self.erc20.initializer(name, symbol);` to initialize the ERC20 component.\n * Finally, it will mint the `initial_supply` to the `deployer` using `self.erc20.mint(deployer, initial_supply);`, fulfilling the requirement of minting to the deployer.\n * The `ERC20Component` from OpenZeppelin typically defaults to 18 decimals, which matches the requirement.\n\n8. **Interface Implementation**: An `#[abi(embed_v0)] impl MY_ERC20Impl of super::IERC20` block will implement the `IERC20` interface. Each function in this implementation will simply delegate the call to the corresponding function within the `self.erc20` component, ensuring adherence to the ERC20 standard.\n\nThis approach ensures that the contract is well-structured, uses audited OpenZeppelin components, and meets all specified requirements.\n\n\n\n```cairo\nuse starknet::ContractAddress;\nuse starknet::ByteArray;\nuse starknet::get_caller_address;\n\n// Define the contract interface\n#[starknet::interface]\npub trait IERC20 {\n fn name(self: @TContractState) -> ByteArray;\n fn symbol(self: @TContractState) -> ByteArray;\n fn decimals(self: @TContractState) -> u8;\n fn total_supply(self: @TContractState) -> u256;\n fn balance_of(self: @TContractState, account: ContractAddress) -> u256;\n fn allowance(self: @TContractState, owner: ContractAddress, spender: ContractAddress) -> u256;\n fn transfer(ref self: TContractState, recipient: ContractAddress, amount: u256);\n fn transfer_from(\n ref self: TContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256\n );\n fn approve(ref self: TContractState, spender: ContractAddress, amount: u256);\n}\n\n// Define the contract module\n#[starknet::contract]\npub mod MY_ERC20 {\n // Always use full paths for core library imports.\n use starknet::ContractAddress;\n use starknet::get_caller_address;\n // Always add all storage imports\n use starknet::storage::*;\n // Add library function depending on context\n use openzeppelin_token::erc20::{ERC20Component, ERC20HooksEmptyImpl};\n use openzeppelin_token::erc20::interface::IERC20;\n\n // Component macro to integrate ERC20 logic\n component!(path: ERC20Component, storage: erc20, event: ERC20Event);\n\n // ERC20 Mixin implementation for external functions\n #[abi(embed_v0)]\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n // Internal implementation for ERC20 logic\n impl ERC20InternalImpl = ERC20Component::InternalImpl;\n\n // Define storage variables\n #[storage]\n pub struct Storage {\n #[substorage(v0)]\n erc20: ERC20Component::Storage,\n }\n\n // Events derive 'Drop, starknet::Event' and the '#[event]' attribute\n #[event]\n #[derive(Drop, starknet::Event)]\n pub enum Event {\n #[flat]\n ERC20Event: ERC20Component::Event,\n }\n\n // Constructor function\n #[constructor]\n fn constructor(ref self: ContractState, initial_supply: u256) {\n let name = \"MY_ERC20\";\n let symbol = \"MYT\"; // A common symbol for MY_ERC20\n let deployer = get_caller_address(); // Get the address of the contract deployer\n\n // Initialize the ERC20 component with name and symbol.\n // OpenZeppelin's ERC20Component defaults to 18 decimals.\n self.erc20.initializer(name, symbol);\n\n // Mint the initial supply to the deployer\n self.erc20.mint(deployer, initial_supply);\n }\n\n // Implement the contract interface\n #[abi(embed_v0)]\n pub impl MY_ERC20Impl of super::IERC20 {\n fn name(self: @ContractState) -> ByteArray {\n self.erc20.name()\n }\n\n fn symbol(self: @ContractState) -> ByteArray {\n self.erc20.symbol()\n }\n\n fn decimals(self: @ContractState) -> u8 {\n self.erc20.decimals()\n }\n\n fn total_supply(self: @ContractState) -> u256 {\n self.erc20.total_supply()\n }\n\n fn balance_of(self: @ContractState, account: ContractAddress) -> u256 {\n self.erc20.balance_of(account)\n }\n\n fn allowance(self: @ContractState, owner: ContractAddress, spender: ContractAddress) -> u256 {\n self.erc20.allowance(owner, spender)\n }\n\n fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) {\n self.erc20.transfer(recipient, amount)\n }\n\n fn transfer_from(\n ref self: ContractState, sender: ContractAddress, recipient: ContractAddress, amount: u256\n ) {\n self.erc20.transfer_from(sender, recipient, amount)\n }\n\n fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) {\n self.erc20.approve(spender, amount)\n }\n }\n}\n```\n\n\n[[ ## completed ## ]]\u001b[0m\n\n\n\n\n\n" - } - ] - }, - { - "id": "gRFv", - "code_hash": "927cbafd36a3c9d15fcfb43aa92911f9", - "outputs": [ - { - "type": "data", - "data": { - "text/plain": "" - } - } - ], - "console": [ - { - "type": "stream", - "name": "stdout", - "text": " Compiling runner_crate v0.1.0 (/private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/Scarb.toml)\nwarn: Unused import: `runner_crate::get_caller_address`\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:3:15\nuse starknet::get_caller_address;\n ^^^^^^^^^^^^^^^^^^\n\nerror[E0006]: Identifier not found.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:2:15\nuse starknet::ByteArray;\n ^^^^^^^^^\n\nerror: Trait has no implementation in context: core::serde::Serde::<>.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:6:1\n#[starknet::interface]\n^^^^^^^^^^^^^^^^^^^^^^\n\nwarn: Plugin diagnostic: Failed to generate ABI: Got unexpected type.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:22:1\n#[starknet::contract]\n^^^^^^^^^^^^^^^^^^^^^\n\nerror: Trait has no implementation in context: openzeppelin_token::erc20::erc20::ERC20Component::ImmutableConfig.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:38:43\n impl ERC20MixinImpl = ERC20Component::ERC20MixinImpl;\n ^^^^^^^^^^^^^^\n\nerror[E0002]: Method `name` could not be called on type `@openzeppelin_token::erc20::erc20::ERC20Component::ComponentState::`.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::name` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::>.\nCandidate `runner_crate::IERC20::name` inference failed with: Trait has no implementation in context: runner_crate::IERC20::>.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::name` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::>.\nCandidate `runner_crate::IERC20::name` inference failed with: Trait has no implementation in context: runner_crate::IERC20::>.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::name` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::.\nCandidate `runner_crate::IERC20::name` inference failed with: Trait has no implementation in context: runner_crate::IERC20::.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:76:24\n self.erc20.name()\n ^^^^\n\nerror[E0002]: Method `symbol` could not be called on type `@openzeppelin_token::erc20::erc20::ERC20Component::ComponentState::`.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::symbol` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::>.\nCandidate `runner_crate::IERC20::symbol` inference failed with: Trait has no implementation in context: runner_crate::IERC20::>.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::symbol` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::>.\nCandidate `runner_crate::IERC20::symbol` inference failed with: Trait has no implementation in context: runner_crate::IERC20::>.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::symbol` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::.\nCandidate `runner_crate::IERC20::symbol` inference failed with: Trait has no implementation in context: runner_crate::IERC20::.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:80:24\n self.erc20.symbol()\n ^^^^^^\n\nerror[E0002]: Method `decimals` could not be called on type `@openzeppelin_token::erc20::erc20::ERC20Component::ComponentState::`.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::decimals` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::>.\nCandidate `runner_crate::IERC20::decimals` inference failed with: Trait has no implementation in context: runner_crate::IERC20::>.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::decimals` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::>.\nCandidate `runner_crate::IERC20::decimals` inference failed with: Trait has no implementation in context: runner_crate::IERC20::>.\nCandidate `openzeppelin_token::erc20::interface::IERC20Mixin::decimals` inference failed with: Trait has no implementation in context: openzeppelin_token::erc20::interface::IERC20Mixin::.\nCandidate `runner_crate::IERC20::decimals` inference failed with: Trait has no implementation in context: runner_crate::IERC20::.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:84:24\n self.erc20.decimals()\n ^^^^^^^^\n\nerror: Unexpected return type. Expected: \"()\", found: \"core::bool\".\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:99:88-101:9\n fn transfer(ref self: ContractState, recipient: ContractAddress, amount: u256) {\n ________________________________________________________________________________________^\n| self.erc20.transfer(recipient, amount)\n| }\n|_________^\n\nerror: Unexpected return type. Expected: \"()\", found: \"core::bool\".\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:105:11-107:9\n ) {\n ___________^\n| self.erc20.transfer_from(sender, recipient, amount)\n| }\n|_________^\n\nerror: Unexpected return type. Expected: \"()\", found: \"core::bool\".\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:109:85-111:9\n fn approve(ref self: ContractState, spender: ContractAddress, amount: u256) {\n _____________________________________________________________________________________^\n| self.erc20.approve(spender, amount)\n| }\n|_________^\n\nerror: Variable not dropped.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:75:9-77:9\n fn name(self: @ContractState) -> ByteArray {\n _________^\n| self.erc20.name()\n| }\n|_________^\nnote: Trait has no implementation in context: core::traits::Drop::<>.\nnote: Trait has no implementation in context: core::traits::Destruct::<>.\n\nerror: Variable not dropped.\n --> /private/var/folders/tm/pbpmnwq91vb2yz8mfnl23q9c0000gn/T/cairo_compile_htt8x0pz/test_project/src/lib.cairo:79:9-81:9\n fn symbol(self: @ContractState) -> ByteArray {\n _________^\n| self.erc20.symbol()\n| }\n|_________^\nnote: Trait has no implementation in context: core::traits::Drop::<>.\nnote: Trait has no implementation in context: core::traits::Destruct::<>.\n\nerror: could not compile `runner_crate` due to previous error\n\n" - } - ] - }, - { - "id": "bkHC", - "code_hash": "8009dba8202d16e971cce959c075fe6f", - "outputs": [], - "console": [] - }, - { - "id": "lEQa", - "code_hash": "941cbef7f7e45e77d7291b92a0f87cbe", - "outputs": [], - "console": [] - }, - { - "id": "PKri", - "code_hash": "563851842d2c50795b932343812bf184", - "outputs": [], - "console": [] - }, - { - "id": "Xref", - "code_hash": null, - "outputs": [], - "console": [] - }, - { - "id": "SFPL", - "code_hash": "548825fd82a6a0d97df464810f637afa", - "outputs": [], - "console": [] - }, - { - "id": "BYtC", - "code_hash": "4bf1772b54447d5da50f13a58f83c45c", - "outputs": [], - "console": [] - }, - { - "id": "RGSE", - "code_hash": "115483c0e7d7fd8384a146e93e4cf08e", - "outputs": [], - "console": [] - }, - { - "id": "Kclp", - "code_hash": "2ac59692c57931173bec92b43ea53154", - "outputs": [], - "console": [] - }, - { - "id": "emfo", - "code_hash": "0902e365ab33340189789b50dc509b2f", - "outputs": [], - "console": [] - }, - { - "id": "Hstk", - "code_hash": "2cd5a626607b09dd723751be2033e781", - "outputs": [], - "console": [] - }, - { - "id": "nWHF", - "code_hash": "d6773889834634746a684216d5811ce9", - "outputs": [], - "console": [] - }, - { - "id": "iLit", - "code_hash": "60a7dc357d1de6e5bc8651825fbb3050", - "outputs": [], - "console": [] - }, - { - "id": "ZHCJ", - "code_hash": null, - "outputs": [], - "console": [] - }, - { - "id": "ROlb", - "code_hash": null, - "outputs": [], - "console": [] - } - ] -} diff --git a/python/src/cairo_coder/optimizers/generation_optimizer_cairo-coder.py b/python/src/cairo_coder/optimizers/generation_optimizer_cairo-coder.py index 334689f..3619618 100644 --- a/python/src/cairo_coder/optimizers/generation_optimizer_cairo-coder.py +++ b/python/src/cairo_coder/optimizers/generation_optimizer_cairo-coder.py @@ -32,7 +32,7 @@ def _(): # mlflow.dspy.autolog() ## Setup VectorDB for document retrieval - embedder = dspy.Embedder("openai/text-embedding-3-large", dimensions=1536, batch_size=512) + embedder = dspy.Embedder("gemini/gemini-embedding-001", dimensions=3072, batch_size=512) vector_store_config = get_vector_store_config() vector_db = SourceFilteredPgVectorRM( db_url=vector_store_config.dsn, @@ -41,7 +41,7 @@ def _(): content_field="content", fields=["id", "content", "metadata"], k=5, # Default k, will be overridden by retriever - embedding_model="text-embedding-3-large", + embedding_model="gemini-embedding-001", include_similarity=True, ) diff --git a/python/src/cairo_coder/optimizers/generation_optimizer_starknet-agent.py b/python/src/cairo_coder/optimizers/generation_optimizer_starknet-agent.py index ea03e02..2337f16 100644 --- a/python/src/cairo_coder/optimizers/generation_optimizer_starknet-agent.py +++ b/python/src/cairo_coder/optimizers/generation_optimizer_starknet-agent.py @@ -32,7 +32,7 @@ def _(): # mlflow.dspy.autolog() ## Setup VectorDB for document retrieval - embedder = dspy.Embedder("openai/text-embedding-3-large", dimensions=1536, batch_size=512) + embedder = dspy.Embedder("gemini/gemini-embedding-001", dimensions=3072, batch_size=512) vector_store_config = get_vector_store_config() vector_db = SourceFilteredPgVectorRM( db_url=vector_store_config.dsn, @@ -41,7 +41,7 @@ def _(): content_field="content", fields=["id", "content", "metadata"], k=5, # Default k, will be overridden by retriever - embedding_model="text-embedding-3-large", + embedding_model="gemini-embedding-001", include_similarity=True, ) @@ -227,7 +227,7 @@ def compute_metrics(gold, pred, trace=None) -> dict: ) if response_rating.score > 1.0: response_rating.score /= 10 - # + # # print(f"Score: {response_rating.score}, feedback: {response_rating.feedback}") return {"score": response_rating.score, "feedback": response_rating.feedback or ""} diff --git a/python/src/cairo_coder/optimizers/retrieval_optimizer.py b/python/src/cairo_coder/optimizers/retrieval_optimizer.py index 787c28a..176eb71 100644 --- a/python/src/cairo_coder/optimizers/retrieval_optimizer.py +++ b/python/src/cairo_coder/optimizers/retrieval_optimizer.py @@ -32,7 +32,7 @@ def _(): # mlflow.dspy.autolog() ## Setup VectorDB for document retrieval - embedder = dspy.Embedder("openai/text-embedding-3-large", dimensions=1536, batch_size=512) + embedder = dspy.Embedder("gemini/gemini-embedding-001", dimensions=3072, batch_size=512) vector_store_config = get_vector_store_config() vector_db = SourceFilteredPgVectorRM( db_url=vector_store_config.dsn, @@ -171,7 +171,7 @@ def compute_metrics(gold, pred, trace=None) -> dict: if pred.retrieved_docs is None or len(pred.retrieved_docs) == 0: return {"score": 0.0, "feedback": "Could not retrieve anything that would answer this query."} with dspy.context( - lm=dspy.LM("openrouter/x-ai/grok-4-fast:free", max_tokens=30000), adapter=XMLAdapter() + lm=dspy.LM("openrouter/x-ai/grok-4-fast", max_tokens=30000), adapter=XMLAdapter() ): response_rating = context_rater( query=gold.query, @@ -343,7 +343,7 @@ def _(RETRIEVER, os): raise FileNotFoundError(f"{compiled_program_path} not found") loading_progr = RETRIEVER() - loading_progr.load(compiled_program_path) + loading_progr.query_processor_program.retrieval_program.load(compiled_program_path) return (loading_progr,) diff --git a/python/src/cairo_coder/server/app.py b/python/src/cairo_coder/server/app.py index b384833..35bb31e 100644 --- a/python/src/cairo_coder/server/app.py +++ b/python/src/cairo_coder/server/app.py @@ -642,7 +642,7 @@ async def lifespan(app: FastAPI): vector_store_config = config.vector_store # TODO: These should not be literal constants like this. - embedder = dspy.Embedder("openai/text-embedding-3-large", dimensions=1536, batch_size=512) + embedder = dspy.Embedder("gemini/gemini-embedding-001", dimensions=3072, batch_size=512) _vector_db = SourceFilteredPgVectorRM( db_url=vector_store_config.dsn, @@ -651,7 +651,7 @@ async def lifespan(app: FastAPI): content_field="content", fields=["id", "content", "metadata"], k=5, # Default k, will be overridden by retriever - embedding_model='text-embedding-3-large', + embedding_model='gemini-embedding-001', include_similarity=True, )