diff --git a/go.mod b/go.mod index f5038dd..f4f4bf4 100644 --- a/go.mod +++ b/go.mod @@ -13,27 +13,12 @@ require ( github.com/gin-gonic/gin v1.10.1 github.com/google/uuid v1.6.0 github.com/olahol/melody v1.3.0 - github.com/tmc/langchaingo v0.1.14-pre.4 + github.com/openai/openai-go/v3 v3.33.0 ) require ( github.com/alecthomas/chroma/v2 v2.20.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.57 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect - github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.24.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 // indirect - github.com/aws/smithy-go v1.22.2 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bytedance/sonic v1.11.6 // indirect @@ -43,32 +28,24 @@ require ( github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/cilium/ebpf v0.11.0 // indirect github.com/cloudwego/base64x v0.1.4 // indirect github.com/cloudwego/iasm v0.2.0 // indirect - github.com/cosiner/argv v0.1.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect - github.com/derekparker/trie/v3 v3.2.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-delve/delve v1.26.3 // indirect - github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/go-dap v0.12.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/haya14busa/goplay v1.0.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.17 // indirect @@ -80,26 +57,22 @@ require ( github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/pkoukk/tiktoken-go v0.1.6 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect - github.com/spf13/cobra v1.10.2 // indirect - github.com/spf13/pflag v1.0.9 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tidwall/sjson v1.2.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yuin/goldmark v1.7.13 // indirect github.com/yuin/goldmark-emoji v1.0.6 // indirect - go.starlark.net v0.0.0-20231101134539-556fd59b42f6 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/crypto v0.41.0 // indirect golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/sync v0.17.0 // indirect golang.org/x/sys v0.37.0 // indirect - golang.org/x/telemetry v0.0.0-20241106142447-58a1122356f5 // indirect golang.org/x/term v0.36.0 // indirect golang.org/x/text v0.30.0 // indirect google.golang.org/protobuf v1.36.3 // indirect diff --git a/go.sum b/go.sum index faedae2..f4a3534 100644 --- a/go.sum +++ b/go.sum @@ -1,50 +1,25 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw= github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA= +github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg= +github.com/alecthomas/repr v0.5.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= -github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= -github.com/aws/aws-sdk-go-v2/config v1.29.4 h1:ObNqKsDYFGr2WxnoXKOhCvTlf3HhwtoGgc+KmZ4H5yg= -github.com/aws/aws-sdk-go-v2/config v1.29.4/go.mod h1:j2/AF7j/qxVmsNIChw1tWfsVKOayJoGRDjg1Tgq7NPk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57 h1:kFQDsbdBAR3GZsB8xA+51ptEnq9TIj3tS4MuP5b+TcQ= -github.com/aws/aws-sdk-go-v2/credentials v1.17.57/go.mod h1:2kerxPUUbTagAr/kkaHiqvj/bcYHzi2qiJS/ZinllU0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 h1:7lOW8NUwE9UZekS1DYoiPdVAqZ6A+LheHWb+mHbNOq8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27/go.mod h1:w1BASFIPOPUae7AgaH4SbjNbfdkxuggLyGfNFTn8ITY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.24.3 h1:GXQrb3kyg4EU94onCRH/oG2IsVjHMNE+IPE4RGkgSa4= -github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.24.3/go.mod h1:PKGlRhLmSZuA6iCbRD1oZKrTJHdm6NWwWBvHxfDNHTA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 h1:c5WJ3iHz7rLIgArznb3JCSQT3uUMiz9DLZhIX+1G8ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.14/go.mod h1:+JJQTxB6N4niArC14YNtxcQtwEqzS3o9Z32n7q33Rfs= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 h1:f1L/JtUkVODD+k1+IiSJUUv8A++2qVr+Xvb3xWXETMU= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13/go.mod h1:tvqlFoja8/s0o+UruA1Nrezo/df0PzdunMDDurUfg6U= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12 h1:fqg6c1KVrc3SYWma/egWue5rKI4G2+M4wMQN2JosNAA= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.12/go.mod h1:7Yn+p66q/jt38qMoVfNvjbm3D89mGBnkwDcijgtih8w= -github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= -github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= @@ -53,48 +28,28 @@ github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4p github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/glamour v1.0.0 h1:AWMLOVFHTsysl4WV8T8QgkQ0s/ZNZo7CiE4WKhk8l08= github.com/charmbracelet/glamour v1.0.0/go.mod h1:DSdohgOBkMr2ZQNhw4LZxSGpx3SvpeujNoXrQyH2hxo= -github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= -github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE= github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA= -github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= -github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= github.com/charmbracelet/x/ansi v0.10.2 h1:ith2ArZS0CJG30cIUfID1LXN7ZFXRCww6RUvAPA+Pzw= github.com/charmbracelet/x/ansi v0.10.2/go.mod h1:HbLdJjQH4UH4AqA2HpRWuWNluRE6zxJH/yteYEYCFa8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI= github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= -github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg= -github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= -github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/derekparker/trie/v3 v3.2.0 h1:fET3Qbp9xSB7yc7tz6Y2GKMNl0SycYFo3cmiRI3Gpf0= -github.com/derekparker/trie/v3 v3.2.0/go.mod h1:P94lW0LPgiaMgKAEQD59IDZD2jMK9paKok8Nli/nQbE= -github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= -github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -103,10 +58,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= -github.com/go-delve/delve v1.26.3 h1:uCWPnLLYmVRXLt0yhw305sCi5lQLHzYB2fZ0FB3KLUI= -github.com/go-delve/delve v1.26.3/go.mod h1:Ua/k2AAu4cLrUXGSRVH1b2Nzq2aCK188b9EYlAojlz4= -github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62 h1:IGtvsNyIuRjl04XAOFGACozgUD7A82UffYxZt4DWbvA= -github.com/go-delve/liner v1.2.3-0.20231231155935-4726ab1d7f62/go.mod h1:biJCRbqp51wS+I92HMqn5H8/A0PAhxn2vyOT+JqhiGI= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -117,26 +68,8 @@ github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBEx github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-dap v0.12.0 h1:rVcjv3SyMIrpaOoTAdFDyHs99CwVOItIJGKLQFQhNeM= -github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -144,10 +77,8 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/haya14busa/goplay v1.0.0 h1:ED4BMrGQ3WH7H3YXrcnWMVzj1xeSepaYTkLh1DtFi/4= -github.com/haya14busa/goplay v1.0.0/go.mod h1:TUcdOVV7TTx0Fo9CmTf16Erfju/DzXTbB70+RYb43h8= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -163,21 +94,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.17 h1:78v8ZlW0bP43XfmAfPsdXcoNCelfMHsDmd/pkENfrjQ= github.com/mattn/go-runewidth v0.0.17/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= @@ -197,32 +120,22 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/olahol/melody v1.3.0 h1:n7UlKiQnxVrgxKoM0d7usZiN+Z0y2lVENtYLgKtXS6s= github.com/olahol/melody v1.3.0/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4= +github.com/openai/openai-go/v3 v3.33.0 h1:aiETRPoLxnk6y3sIakXAdRCvtcLhdhBqHqIvEdOkEuc= +github.com/openai/openai-go/v3 v3.33.0/go.mod h1:cdufnVK14cWcT9qA1rRtrXx4FTRsgbDPW7Ia7SS5cZo= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/pkoukk/tiktoken-go v0.1.6 h1:JF0TlJzhTbrI30wCvFuiw6FzP2+/bR+FIxUdgEAcUsw= -github.com/pkoukk/tiktoken-go v0.1.6/go.mod h1:9NiV+i9mJKGj1rYOT+njbv+ZwA/zJxYdewGl6qVatpg= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= -github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= -github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -230,11 +143,18 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tmc/langchaingo v0.1.14-pre.4 h1:zt4/Cw+FecTfAQggXicWIXB793002FgadJIHxSpj37A= -github.com/tmc/langchaingo v0.1.14-pre.4/go.mod h1:xGqIATL4itqqEAVwSF5xVh4ZuIP7gOE0dyoqe3quvzw= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= @@ -245,97 +165,32 @@ github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA= github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= github.com/yuin/goldmark-emoji v1.0.6 h1:QWfF2FYaXwL74tfGOW5izeiZepUDroDJfWubQI9HTHs= github.com/yuin/goldmark-emoji v1.0.6/go.mod h1:ukxJDKFpdFb5x0a5HqbdlcKtebh086iJpI31LTKmWuA= -go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 h1:Ss6D3hLXTM0KobyBYEAygXzFfGcjnmfEJOBgSbemCtg= -go.starlark.net v0.0.0-20230302034142-4b1e35fe2254/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= -go.starlark.net v0.0.0-20231101134539-556fd59b42f6 h1:+eC0F/k4aBLC4szgOcjd7bDTEnpxADJyWJE0yowgM3E= -go.starlark.net v0.0.0-20231101134539-556fd59b42f6/go.mod h1:LcLNIzVOMp4oV+uusnpk+VU+SzXaJakUuBjoCSWH5dM= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= -golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20241106142447-58a1122356f5 h1:TCDqnvbBsFapViksHcHySl/sW4+rTGNIAoJJesHRuMM= -golang.org/x/telemetry v0.0.0-20241106142447-58a1122356f5/go.mod h1:8nZWdGp9pq73ZI//QJyckMQab3yq7hoWi7SI0UIusVI= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/internal/agents/chat.go b/internal/agents/chat.go index 4c3253b..a151a39 100644 --- a/internal/agents/chat.go +++ b/internal/agents/chat.go @@ -8,7 +8,7 @@ import ( "codeactor/internal/tools" "codeactor/internal/globalctx" - "github.com/tmc/langchaingo/llms" + "codeactor/internal/llm" ) //go:embed chat.prompt.md @@ -21,7 +21,7 @@ type ChatAgent struct { maxSteps int } -func NewChatAgent(globalCtx *globalctx.GlobalCtx, llm llms.LLM, maxSteps int) *ChatAgent { +func NewChatAgent(globalCtx *globalctx.GlobalCtx, llm llm.Engine, maxSteps int) *ChatAgent { // Build a minimal tool set for ChatAgent: micro_agent for sub-LLM reasoning, // thinking for cognitive reflection, and agent_exit for clean termination. var toolDefs []ToolDefinition diff --git a/internal/agents/coding.go b/internal/agents/coding.go index 9930b98..756f47a 100644 --- a/internal/agents/coding.go +++ b/internal/agents/coding.go @@ -9,7 +9,7 @@ import ( "codeactor/internal/tools" "codeactor/internal/globalctx" - "github.com/tmc/langchaingo/llms" + "codeactor/internal/llm" ) //go:embed coding.prompt.md @@ -28,7 +28,7 @@ type CodingAgent struct { maxSteps int } -func NewCodingAgent(globalCtx *globalctx.GlobalCtx, llm llms.LLM, maxSteps int) *CodingAgent { +func NewCodingAgent(globalCtx *globalctx.GlobalCtx, llm llm.Engine, maxSteps int) *CodingAgent { var toolDefs []ToolDefinition if err := json.Unmarshal(ToolsJSON, &toolDefs); err != nil { slog.Error("Failed to unmarshal coding tools", "error", err) diff --git a/internal/agents/coding.prompt.md b/internal/agents/coding.prompt.md index 258437b..d41781b 100644 --- a/internal/agents/coding.prompt.md +++ b/internal/agents/coding.prompt.md @@ -53,24 +53,6 @@ You have access to the following tools. You must use them to interact with the s * Call tools directly for actions. * In the final text response, summarize changes and guide the user on next steps. -# Few-Shot Examples - -### Example -User: "How is the user authentication implemented?" -Thinking Tool: "I need to explore the codebase to understand the authentication implementation. I will start by searching for relevant code." -Tool Call: `semantic_search(query="user authentication implementation")` -Tool Call: `query_code_skeleton(file_path="/internal/auth/service.go")` -Tool Call: `query_code_snippet(file_path="/internal/auth/service.go", symbol="Login")` -Response: "I found the user authentication logic in `/internal/auth/service.go`. The `Login` function handles..." - -### Example -User: "The tests are failing in `utils.js`." -Thinking Tool: "I need to read `utils.js` and the test output to understand the failure." -Tool Call: `run_bash(command="npm test")` -Tool Call: `read_file(file_path="utils.js")` -Thinking Tool: "The error is a TypeError on line 10. The variable `x` is undefined. I will fix it by initializing `x`." -Tool Call: `search_replace_in_file(file_path="utils.js", ...)` -Response: "I fixed the TypeError in `utils.js`. Tests should pass now." # Core Directives * **Be Proactive**: Don't wait for the user to drive every step. Take initiative. diff --git a/internal/agents/conductor.go b/internal/agents/conductor.go index eb69590..a0a6dd1 100644 --- a/internal/agents/conductor.go +++ b/internal/agents/conductor.go @@ -8,11 +8,10 @@ import ( "log/slog" "strings" + "codeactor/internal/llm" "codeactor/internal/tools" "codeactor/internal/globalctx" "codeactor/internal/memory" - - "github.com/tmc/langchaingo/llms" ) //go:embed conductor.prompt.md @@ -51,7 +50,7 @@ type ConductorAgent struct { customAgents map[string]*CustomAgent // delegate_ → agent design } -func NewConductorAgent(globalCtx *globalctx.GlobalCtx, llm llms.LLM, repo *RepoAgent, coding *CodingAgent, chat *ChatAgent, meta *MetaAgent, maxSteps int, disabledAgents map[string]bool, metaRetryCount int) *ConductorAgent { +func NewConductorAgent(globalCtx *globalctx.GlobalCtx, engine llm.Engine, repo *RepoAgent, coding *CodingAgent, chat *ChatAgent, meta *MetaAgent, maxSteps int, disabledAgents map[string]bool, metaRetryCount int) *ConductorAgent { // self-reference for closures that need the ConductorAgent after construction var self *ConductorAgent delegateRepo := tools.NewAdapter("delegate_repo", "Delegate analysis task to Repo-Agent", func(ctx context.Context, params map[string]interface{}) (interface{}, error) { @@ -254,7 +253,7 @@ func NewConductorAgent(globalCtx *globalctx.GlobalCtx, llm llms.LLM, repo *RepoA tools.SetGuardOnAdapters(delegateAdapters, globalCtx.Guard) self = &ConductorAgent{ - BaseAgent: BaseAgent{LLM: llm, Publisher: globalCtx.Publisher}, + BaseAgent: BaseAgent{LLM: engine, Publisher: globalCtx.Publisher}, RepoAgent: repo, CodingAgent: coding, ChatAgent: chat, @@ -496,46 +495,48 @@ func (a *ConductorAgent) executeCustomAgent(ctx context.Context, ca *CustomAgent return RunAgentLoop(ctx, cfg) } -func convertToolCalls(tcs []llms.ToolCall) []memory.ToolCallData { +func convertToolCalls(tcs []llm.ToolCall) []memory.ToolCallData { var res []memory.ToolCallData for _, tc := range tcs { res = append(res, memory.ToolCallData{ ID: tc.ID, - Type: string(tc.Type), + Type: tc.Type, Function: memory.ToolCallFunction{ - Name: tc.FunctionCall.Name, - Arguments: json.RawMessage(tc.FunctionCall.Arguments), + Name: tc.Function.Name, + Arguments: json.RawMessage(tc.Function.Arguments), }, }) } return res } -func convertMemoryMessageToLLMSMessage(msg memory.ChatMessage) llms.MessageContent { - role := llms.ChatMessageTypeHuman +func convertMemoryMessageToLLMSMessage(msg memory.ChatMessage) llm.Message { + role := llm.RoleUser switch msg.Type { case memory.MessageTypeSystem: - role = llms.ChatMessageTypeSystem + role = llm.RoleSystem case memory.MessageTypeHuman: - role = llms.ChatMessageTypeHuman + role = llm.RoleUser case memory.MessageTypeAssistant: - role = llms.ChatMessageTypeAI + role = llm.RoleAssistant case memory.MessageTypeTool: - role = llms.ChatMessageTypeTool + role = llm.RoleTool } - parts := []llms.ContentPart{} + result := llm.Message{ + Role: role, + } if msg.Content != "" && msg.Type != memory.MessageTypeTool { - parts = append(parts, llms.TextPart(msg.Content)) + result.Content = msg.Content } if len(msg.ToolCalls) > 0 { for _, tc := range msg.ToolCalls { - parts = append(parts, llms.ToolCall{ + result.ToolCalls = append(result.ToolCalls, llm.ToolCall{ ID: tc.ID, - Type: string(tc.Type), - FunctionCall: &llms.FunctionCall{ + Type: tc.Type, + Function: llm.FunctionCall{ Name: tc.Function.Name, Arguments: string(tc.Function.Arguments), }, @@ -544,16 +545,11 @@ func convertMemoryMessageToLLMSMessage(msg memory.ChatMessage) llms.MessageConte } if msg.Type == memory.MessageTypeTool && msg.ToolCallID != nil { - parts = append(parts, llms.ToolCallResponse{ - ToolCallID: *msg.ToolCallID, - Content: msg.Content, - }) + result.ToolCallID = *msg.ToolCallID + result.Content = msg.Content } - return llms.MessageContent{ - Role: role, - Parts: parts, - } + return result } func (a *ConductorAgent) Run(ctx context.Context, input string, mem *memory.ConversationMemory) (string, error) { @@ -566,7 +562,7 @@ func (a *ConductorAgent) Run(ctx context.Context, input string, mem *memory.Conv } } - var messages []llms.MessageContent + var messages []llm.Message // Always start with System Prompt (with any registered custom agents appended) systemPrompt := a.GlobalCtx.FormatPrompt(conductorPrompt) @@ -577,9 +573,9 @@ func (a *ConductorAgent) Run(ctx context.Context, input string, mem *memory.Conv } systemPrompt += "\nUse these agents via their delegate tools for tasks matching their specializations.\n" } - messages = append(messages, llms.MessageContent{ - Role: llms.ChatMessageTypeSystem, - Parts: []llms.ContentPart{llms.TextPart(systemPrompt)}, + messages = append(messages, llm.Message{ + Role: llm.RoleSystem, + Content: systemPrompt, }) if mem != nil { @@ -591,68 +587,65 @@ func (a *ConductorAgent) Run(ctx context.Context, input string, mem *memory.Conv messages = append(messages, convertMemoryMessageToLLMSMessage(m)) } } else { - messages = append(messages, llms.MessageContent{ - Role: llms.ChatMessageTypeHuman, - Parts: []llms.ContentPart{llms.TextPart(input)}, + messages = append(messages, llm.Message{ + Role: llm.RoleUser, + Content: input, }) } - llmTools := make([]llms.Tool, len(a.Adapters)) + toolDefs := make([]llm.ToolDef, len(a.Adapters)) for i, ad := range a.Adapters { - llmTools[i] = ad.ToLLMSTool() + toolDefs[i] = ad.ToToolDef() } for i := 0; i < a.maxSteps; i++ { slog.Debug("ConductorAgent calling LLM", "step", i, "messages", messages) - resp, err := a.LLM.GenerateContent(ctx, messages, llms.WithTools(llmTools)) + resp, err := a.LLM.GenerateContent(ctx, messages, toolDefs, nil) if err != nil { slog.Error("ConductorAgent LLM error", "error", err, "step", i) return "", err } - msg := resp.Choices[0] - slog.Debug("ConductorAgent LLM response", "step", i, "message", msg) + choice := resp.Choices[0] + slog.Debug("ConductorAgent LLM response", "step", i, "content", choice.Content, "tool_calls", len(choice.ToolCalls)) - if msg.Content != "" { + if choice.Content != "" { if a.Publisher != nil { - a.Publisher.Publish("ai_response", msg.Content, a.Name()) + a.Publisher.Publish("ai_response", choice.Content, a.Name()) } } if mem != nil { - mem.AddAssistantMessage(msg.Content, convertToolCalls(msg.ToolCalls)) + mem.AddAssistantMessage(choice.Content, convertToolCalls(choice.ToolCalls)) } - parts := []llms.ContentPart{llms.TextPart(msg.Content)} - for _, tc := range msg.ToolCalls { - parts = append(parts, tc) - } - - messages = append(messages, llms.MessageContent{ - Role: llms.ChatMessageTypeAI, - Parts: parts, + messages = append(messages, llm.Message{ + Role: llm.RoleAssistant, + Content: choice.Content, + Reasoning: choice.Reasoning, + ToolCalls: choice.ToolCalls, }) - if len(msg.ToolCalls) == 0 { - return msg.Content, nil + if len(choice.ToolCalls) == 0 { + return choice.Content, nil } - for _, tc := range msg.ToolCalls { + for _, tc := range choice.ToolCalls { var toolResult string var err error found := false if a.Publisher != nil { a.Publisher.Publish("tool_call_start", map[string]interface{}{ - "tool_name": tc.FunctionCall.Name, - "arguments": tc.FunctionCall.Arguments, + "tool_name": tc.Function.Name, + "arguments": tc.Function.Arguments, "tool_call_id": tc.ID, }, a.Name()) } for _, t := range a.Adapters { - if t.Name() == tc.FunctionCall.Name { + if t.Name() == tc.Function.Name { found = true - toolResult, err = t.Call(ctx, tc.FunctionCall.Arguments) + toolResult, err = t.Call(ctx, tc.Function.Arguments) if err != nil { toolResult = fmt.Sprintf("Error: %v", err) } else if t.Name() == "delegate_repo" { @@ -669,12 +662,12 @@ func (a *ConductorAgent) Run(ctx context.Context, input string, mem *memory.Conv } } if !found { - toolResult = fmt.Sprintf("Tool %s not found", tc.FunctionCall.Name) + toolResult = fmt.Sprintf("Tool %s not found", tc.Function.Name) } if a.Publisher != nil { a.Publisher.Publish("tool_call_result", map[string]interface{}{ - "tool_name": tc.FunctionCall.Name, + "tool_name": tc.Function.Name, "result": toolResult, "tool_call_id": tc.ID, }, a.Name()) @@ -684,17 +677,13 @@ func (a *ConductorAgent) Run(ctx context.Context, input string, mem *memory.Conv mem.AddToolMessage(toolResult, tc.ID) } - messages = append(messages, llms.MessageContent{ - Role: llms.ChatMessageTypeTool, - Parts: []llms.ContentPart{ - llms.ToolCallResponse{ - ToolCallID: tc.ID, - Name: tc.FunctionCall.Name, - Content: toolResult, - }, - }, + messages = append(messages, llm.Message{ + Role: llm.RoleTool, + Content: toolResult, + ToolCallID: tc.ID, + ToolName: tc.Function.Name, }) - if tc.FunctionCall.Name == "agent_exit" { + if tc.Function.Name == "agent_exit" { return "Task completed successfully", nil } diff --git a/internal/agents/conductor.prompt.md b/internal/agents/conductor.prompt.md index 1ac191d..e00e87a 100644 --- a/internal/agents/conductor.prompt.md +++ b/internal/agents/conductor.prompt.md @@ -115,7 +115,7 @@ This block is your "Inner Monologue" to reason about the current state and updat - The `Thought Process` block MUST be in the language specified in **Language Instructions**. - The arguments for `agent_exit` (reason) MUST be in the language specified in **Language Instructions**. -After the `Thought Process` block, you MUST issue exactly **ONE** Tool Call (`delegate_repo`, `delegate_coding`, `delegate_chat`, `delegate_meta`, `delegate_` for custom agents, `agent_exit`). +After the `Thought Process` block, you MUST issue exactly **ONE** Tool Call (`delegate_repo`, `delegate_coding`, `delegate_chat`, `delegate_meta`, `delegate_{name}` for custom agents, `agent_exit`). # Final Instruction - Think deeply inside `Thought Process` block before acting. diff --git a/internal/agents/conductor_test.go b/internal/agents/conductor_test.go index 4aa764c..43141c9 100644 --- a/internal/agents/conductor_test.go +++ b/internal/agents/conductor_test.go @@ -7,32 +7,23 @@ import ( "strings" "testing" + "codeactor/internal/llm" "codeactor/internal/tools" "codeactor/internal/globalctx" "codeactor/internal/memory" - - "github.com/tmc/langchaingo/llms" ) -// ─── Mock LLM ──────────────────────────────────────────────────────────────── +// ─── Mock Engine ────────────────────────────────────────────────────────────── -type mockLLM struct { - generateContent func(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) - call func(ctx context.Context, prompt string, options ...llms.CallOption) (string, error) +type mockEngine struct { + generateContent func(ctx context.Context, messages []llm.Message, tools []llm.ToolDef, opts *llm.CallOptions) (*llm.Response, error) } -func (m *mockLLM) GenerateContent(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) { +func (m *mockEngine) GenerateContent(ctx context.Context, messages []llm.Message, tools []llm.ToolDef, opts *llm.CallOptions) (*llm.Response, error) { if m.generateContent != nil { - return m.generateContent(ctx, messages, options...) - } - return &llms.ContentResponse{Choices: []*llms.ContentChoice{{Content: ""}}}, nil -} - -func (m *mockLLM) Call(ctx context.Context, prompt string, options ...llms.CallOption) (string, error) { - if m.call != nil { - return m.call(ctx, prompt, options...) + return m.generateContent(ctx, messages, tools, opts) } - return "", nil + return &llm.Response{Choices: []llm.Choice{{Content: ""}}}, nil } // ─── Test Helpers ──────────────────────────────────────────────────────────── @@ -59,7 +50,8 @@ func newTestGlobalCtx(workDir string) *globalctx.GlobalCtx { func newTestConductorAgent(t *testing.T, workDir string) *ConductorAgent { t.Helper() gctx := newTestGlobalCtx(workDir) - return NewConductorAgent(gctx, &mockLLM{}, nil, nil, nil, nil, 10, nil, 3) + engine := &mockEngine{} + return NewConductorAgent(gctx, engine, nil, nil, nil, nil, 10, nil, 3) } // makeMetaOutput builds a valid Meta-Agent JSON output string. @@ -344,10 +336,10 @@ func TestCustomAgentDelegateTool_Execution(t *testing.T) { gctx := newTestGlobalCtx(workDir) // Use a mock LLM that returns a simple response for the custom agent - customLLM := &mockLLM{ - generateContent: func(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) { - return &llms.ContentResponse{ - Choices: []*llms.ContentChoice{ + customEngine := &mockEngine{ + generateContent: func(ctx context.Context, messages []llm.Message, tools []llm.ToolDef, opts *llm.CallOptions) (*llm.Response, error) { + return &llm.Response{ + Choices: []llm.Choice{ {Content: "Custom agent task completed."}, }, }, nil @@ -355,7 +347,7 @@ func TestCustomAgentDelegateTool_Execution(t *testing.T) { } // Build conductor with mocked LLM - conductor := NewConductorAgent(gctx, customLLM, nil, nil, nil, nil, 10, nil, 3) + conductor := NewConductorAgent(gctx, customEngine, nil, nil, nil, nil, 10, nil, 3) ca := &CustomAgent{ Name: "test_executor", @@ -393,18 +385,18 @@ func TestCustomAgentDelegateTool_FinishTerminates(t *testing.T) { gctx := newTestGlobalCtx(workDir) callCount := 0 - customLLM := &mockLLM{ - generateContent: func(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) { + customEngine := &mockEngine{ + generateContent: func(ctx context.Context, messages []llm.Message, tools []llm.ToolDef, opts *llm.CallOptions) (*llm.Response, error) { callCount++ if callCount == 1 { // First call: use agent_exit to complete - return &llms.ContentResponse{ - Choices: []*llms.ContentChoice{{ + return &llm.Response{ + Choices: []llm.Choice{{ Content: "Task is complete.", - ToolCalls: []llms.ToolCall{{ + ToolCalls: []llm.ToolCall{{ ID: "call_agent_exit", Type: "function", - FunctionCall: &llms.FunctionCall{ + Function: llm.FunctionCall{ Name: "agent_exit", Arguments: `{"reason": "Test completed successfully"}`, }, @@ -412,13 +404,13 @@ func TestCustomAgentDelegateTool_FinishTerminates(t *testing.T) { }}, }, nil } - return &llms.ContentResponse{ - Choices: []*llms.ContentChoice{{Content: "Should not reach here"}}, + return &llm.Response{ + Choices: []llm.Choice{{Content: "Should not reach here"}}, }, nil }, } - conductor := NewConductorAgent(gctx, customLLM, nil, nil, nil, nil, 10, nil, 3) + conductor := NewConductorAgent(gctx, customEngine, nil, nil, nil, nil, 10, nil, 3) ca := &CustomAgent{ Name: "finisher", @@ -524,11 +516,11 @@ func TestSystemPrompt_WithCustomAgents(t *testing.T) { // ─── delegate_meta Full Integration Tests ──────────────────────────────────── // metaAgentMockLLM returns a Meta-Agent-style response based on the input. -func metaAgentMockLLM(responseContent string) *mockLLM { - return &mockLLM{ - generateContent: func(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) { - return &llms.ContentResponse{ - Choices: []*llms.ContentChoice{{Content: responseContent}}, +func metaAgentMockLLM(responseContent string) *mockEngine { + return &mockEngine{ + generateContent: func(ctx context.Context, messages []llm.Message, tools []llm.ToolDef, opts *llm.CallOptions) (*llm.Response, error) { + return &llm.Response{ + Choices: []llm.Choice{{Content: responseContent}}, }, nil }, } @@ -549,7 +541,7 @@ func TestDelegateMeta_DynamicRegistration(t *testing.T) { metaAgent := NewMetaAgent(gctx, metaAgentMockLLM(metaOutput)) // ConductorAgent - conductor := NewConductorAgent(gctx, &mockLLM{}, nil, nil, nil, metaAgent, 10, nil, 3) + conductor := NewConductorAgent(gctx, &mockEngine{}, nil, nil, nil, metaAgent, 10, nil, 3) initialAdapterCount := len(conductor.Adapters) // Find and call delegate_meta tool @@ -625,7 +617,7 @@ func TestDelegateMeta_DuplicateRegistrationPrevented(t *testing.T) { ) metaAgent := NewMetaAgent(gctx, metaAgentMockLLM(metaOutput)) - conductor := NewConductorAgent(gctx, &mockLLM{}, nil, nil, nil, metaAgent, 10, nil, 3) + conductor := NewConductorAgent(gctx, &mockEngine{}, nil, nil, nil, metaAgent, 10, nil, 3) // Call delegate_meta twice with the same agent design var delegateMeta *tools.Adapter @@ -665,7 +657,7 @@ func TestDelegateMeta_ParseFailure_ReturnsRawOutput(t *testing.T) { // Meta-Agent returns malformed output (no execution_result block) malformedOutput := "Just some plain text without structured blocks." metaAgent := NewMetaAgent(gctx, metaAgentMockLLM(malformedOutput)) - conductor := NewConductorAgent(gctx, &mockLLM{}, nil, nil, nil, metaAgent, 10, nil, 3) + conductor := NewConductorAgent(gctx, &mockEngine{}, nil, nil, nil, metaAgent, 10, nil, 3) var delegateMeta *tools.Adapter for _, ad := range conductor.Adapters { @@ -708,7 +700,7 @@ func TestDelegateMeta_EmptyAgentName_NoRegistration(t *testing.T) { []string{"read_file"}, ) metaAgent := NewMetaAgent(gctx, metaAgentMockLLM(metaOutput)) - conductor := NewConductorAgent(gctx, &mockLLM{}, nil, nil, nil, metaAgent, 10, nil, 3) + conductor := NewConductorAgent(gctx, &mockEngine{}, nil, nil, nil, metaAgent, 10, nil, 3) var delegateMeta *tools.Adapter for _, ad := range conductor.Adapters { @@ -738,7 +730,7 @@ func TestDelegateMeta_NoAgentDesign_NoRegistration(t *testing.T) { output := `{"thinking": "designing...", "agent_name": "Test Agent", "tools_used": ["read_file"], "result": {"key": "value"}}` metaAgent := NewMetaAgent(gctx, metaAgentMockLLM(output)) - conductor := NewConductorAgent(gctx, &mockLLM{}, nil, nil, nil, metaAgent, 10, nil, 3) + conductor := NewConductorAgent(gctx, &mockEngine{}, nil, nil, nil, metaAgent, 10, nil, 3) var delegateMeta *tools.Adapter for _, ad := range conductor.Adapters { @@ -774,14 +766,14 @@ func TestConvertMemoryMessageToLLMSMessage_ToolMessage(t *testing.T) { llmMsg := convertMemoryMessageToLLMSMessage(msg) - if len(llmMsg.Parts) != 1 { - t.Errorf("Expected 1 part, got %d", len(llmMsg.Parts)) + if llmMsg.Role != llm.RoleTool { + t.Errorf("Expected role %s, got %s", llm.RoleTool, llmMsg.Role) } - - part := llmMsg.Parts[0] - _, ok := part.(llms.ToolCallResponse) - if !ok { - t.Errorf("Expected part to be ToolCallResponse, got %T", part) + if llmMsg.ToolCallID != toolCallID { + t.Errorf("Expected ToolCallID %q, got %q", toolCallID, llmMsg.ToolCallID) + } + if llmMsg.Content != content { + t.Errorf("Expected Content %q, got %q", content, llmMsg.Content) } } @@ -803,18 +795,20 @@ func TestConvertMemoryMessageToLLMSMessage_AssistantWithToolCalls(t *testing.T) llmMsg := convertMemoryMessageToLLMSMessage(msg) - if len(llmMsg.Parts) != 2 { - t.Errorf("Expected 2 parts (text + tool call), got %d", len(llmMsg.Parts)) + if llmMsg.Role != llm.RoleAssistant { + t.Errorf("Expected role %s, got %s", llm.RoleAssistant, llmMsg.Role) } - - // First part should be text (TextContent) - if _, ok := llmMsg.Parts[0].(llms.TextContent); !ok { - t.Errorf("Expected first part to be TextContent, got %T", llmMsg.Parts[0]) + if llmMsg.Content != "Let me check that." { + t.Errorf("Expected Content to be set, got %q", llmMsg.Content) } - - // Second part should be ToolCall - if _, ok := llmMsg.Parts[1].(llms.ToolCall); !ok { - t.Errorf("Expected second part to be ToolCall") + if len(llmMsg.ToolCalls) != 1 { + t.Fatalf("Expected 1 ToolCall, got %d", len(llmMsg.ToolCalls)) + } + if llmMsg.ToolCalls[0].ID != "call_1" { + t.Errorf("Expected ToolCall ID 'call_1', got %q", llmMsg.ToolCalls[0].ID) + } + if llmMsg.ToolCalls[0].Function.Name != "read_file" { + t.Errorf("Expected ToolCall function name 'read_file', got %q", llmMsg.ToolCalls[0].Function.Name) } } diff --git a/internal/agents/executor.go b/internal/agents/executor.go index 32d1c78..7980ca6 100644 --- a/internal/agents/executor.go +++ b/internal/agents/executor.go @@ -5,10 +5,9 @@ import ( "fmt" "log/slog" + "codeactor/internal/llm" "codeactor/internal/tools" "codeactor/pkg/messaging" - - "github.com/tmc/langchaingo/llms" ) // ExecutorConfig holds the configuration for running an LLM-tool agent loop. @@ -16,7 +15,7 @@ type ExecutorConfig struct { SystemPrompt string UserInput string Adapters []*tools.Adapter - LLM llms.LLM + LLM llm.Engine MaxSteps int Publisher *messaging.MessagePublisher AgentName string @@ -32,71 +31,72 @@ type ExecutorConfig struct { // RunAgentLoop runs the standard LLM-tool interaction loop. // It returns the final text response from the LLM. func RunAgentLoop(ctx context.Context, cfg ExecutorConfig) (string, error) { - systemRole := llms.ChatMessageTypeSystem + systemRole := llm.RoleSystem if cfg.SystemAsHuman { - systemRole = llms.ChatMessageTypeHuman + systemRole = llm.RoleUser } - messages := []llms.MessageContent{ + messages := []llm.Message{ { - Role: systemRole, - Parts: []llms.ContentPart{llms.TextPart(cfg.SystemPrompt)}, + Role: systemRole, + Content: cfg.SystemPrompt, }, { - Role: llms.ChatMessageTypeHuman, - Parts: []llms.ContentPart{llms.TextPart(cfg.UserInput)}, + Role: llm.RoleUser, + Content: cfg.UserInput, }, } - llmTools := make([]llms.Tool, len(cfg.Adapters)) + toolDefs := make([]llm.ToolDef, len(cfg.Adapters)) for i, ad := range cfg.Adapters { - llmTools[i] = ad.ToLLMSTool() + toolDefs[i] = ad.ToToolDef() } + opts := &llm.CallOptions{} + for i := 0; i < cfg.MaxSteps; i++ { slog.Debug("AgentExecutor calling LLM", "agent", cfg.AgentName, "step", i) - resp, err := cfg.LLM.GenerateContent(ctx, messages, llms.WithTools(llmTools)) + resp, err := cfg.LLM.GenerateContent(ctx, messages, toolDefs, opts) if err != nil { slog.Error("AgentExecutor LLM error", "agent", cfg.AgentName, "error", err, "step", i) return "", err } - msg := resp.Choices[0] - if msg.Content != "" && cfg.Publisher != nil { - cfg.Publisher.Publish("ai_response", msg.Content, cfg.AgentName) + choice := resp.Choices[0] + if choice.Content != "" && cfg.Publisher != nil { + cfg.Publisher.Publish("ai_response", choice.Content, cfg.AgentName) } - parts := []llms.ContentPart{llms.TextPart(msg.Content)} - for _, tc := range msg.ToolCalls { - parts = append(parts, tc) + // Build assistant message + assistantMsg := llm.Message{ + Role: llm.RoleAssistant, + Content: choice.Content, + Reasoning: choice.Reasoning, + ToolCalls: choice.ToolCalls, } + messages = append(messages, assistantMsg) - messages = append(messages, llms.MessageContent{ - Role: llms.ChatMessageTypeAI, - Parts: parts, - }) - - if len(msg.ToolCalls) == 0 { - return msg.Content, nil + if len(choice.ToolCalls) == 0 { + return choice.Content, nil } - for _, tc := range msg.ToolCalls { + for _, tc := range choice.ToolCalls { var toolResult string var callErr error found := false if cfg.Publisher != nil { cfg.Publisher.Publish("tool_call_start", map[string]interface{}{ - "tool_name": tc.FunctionCall.Name, - "arguments": tc.FunctionCall.Arguments, + "tool_name": tc.Function.Name, + "arguments": tc.Function.Arguments, "tool_call_id": tc.ID, }, cfg.AgentName) } for _, t := range cfg.Adapters { - if t.Name() == tc.FunctionCall.Name { + if t.Name() == tc.Function.Name { found = true - toolResult, callErr = t.Call(ctx, tc.FunctionCall.Arguments) + toolResult, callErr = t.Call(ctx, tc.Function.Arguments) if callErr != nil { toolResult = fmt.Sprintf("Error: %v", callErr) } @@ -104,33 +104,29 @@ func RunAgentLoop(ctx context.Context, cfg ExecutorConfig) (string, error) { } } if !found { - toolResult = fmt.Sprintf("Tool %s not found", tc.FunctionCall.Name) + toolResult = fmt.Sprintf("Tool %s not found", tc.Function.Name) } if cfg.OnToolResult != nil { - cfg.OnToolResult(tc.FunctionCall.Name, toolResult) + cfg.OnToolResult(tc.Function.Name, toolResult) } if cfg.Publisher != nil { cfg.Publisher.Publish("tool_call_result", map[string]interface{}{ - "tool_name": tc.FunctionCall.Name, + "tool_name": tc.Function.Name, "result": toolResult, "tool_call_id": tc.ID, }, cfg.AgentName) } - messages = append(messages, llms.MessageContent{ - Role: llms.ChatMessageTypeTool, - Parts: []llms.ContentPart{ - llms.ToolCallResponse{ - ToolCallID: tc.ID, - Name: tc.FunctionCall.Name, - Content: toolResult, - }, - }, + messages = append(messages, llm.Message{ + Role: llm.RoleTool, + Content: toolResult, + ToolCallID: tc.ID, + ToolName: tc.Function.Name, }) - if cfg.StopOnFinish && tc.FunctionCall.Name == "agent_exit" { + if cfg.StopOnFinish && tc.Function.Name == "agent_exit" { return toolResult, nil } } diff --git a/internal/agents/meta.go b/internal/agents/meta.go index 4d5b256..ebad7bd 100644 --- a/internal/agents/meta.go +++ b/internal/agents/meta.go @@ -7,8 +7,7 @@ import ( "log/slog" "codeactor/internal/globalctx" - - "github.com/tmc/langchaingo/llms" + "codeactor/internal/llm" ) //go:embed meta.prompt.md @@ -22,7 +21,7 @@ type MetaAgent struct { GlobalCtx *globalctx.GlobalCtx } -func NewMetaAgent(globalCtx *globalctx.GlobalCtx, llm llms.LLM) *MetaAgent { +func NewMetaAgent(globalCtx *globalctx.GlobalCtx, llm llm.Engine) *MetaAgent { return &MetaAgent{ BaseAgent: BaseAgent{ LLM: llm, @@ -41,19 +40,19 @@ func (a *MetaAgent) Name() string { func (a *MetaAgent) Run(ctx context.Context, input string) (string, error) { systemPrompt := a.GlobalCtx.FormatPrompt(metaPrompt) - messages := []llms.MessageContent{ + messages := []llm.Message{ { - Role: llms.ChatMessageTypeSystem, - Parts: []llms.ContentPart{llms.TextPart(systemPrompt)}, + Role: llm.RoleSystem, + Content: systemPrompt, }, { - Role: llms.ChatMessageTypeHuman, - Parts: []llms.ContentPart{llms.TextPart(input)}, + Role: llm.RoleUser, + Content: input, }, } slog.Debug("MetaAgent calling LLM (design-only, no tools)", "input", input) - resp, err := a.LLM.GenerateContent(ctx, messages) + resp, err := a.LLM.GenerateContent(ctx, messages, nil, nil) if err != nil { slog.Error("MetaAgent LLM error", "error", err) return "", fmt.Errorf("MetaAgent LLM call failed: %w", err) diff --git a/internal/agents/repo.go b/internal/agents/repo.go index 0527dcb..63b3aa8 100644 --- a/internal/agents/repo.go +++ b/internal/agents/repo.go @@ -15,7 +15,7 @@ import ( "codeactor/internal/globalctx" "codeactor/pkg/messaging" - "github.com/tmc/langchaingo/llms" + "codeactor/internal/llm" ) //go:embed repo.prompt.md @@ -55,7 +55,7 @@ type RepoAgent struct { maxSteps int } -func NewRepoAgent(globalCtx *globalctx.GlobalCtx, llm llms.LLM, publisher *messaging.MessagePublisher, maxSteps int) *RepoAgent { +func NewRepoAgent(globalCtx *globalctx.GlobalCtx, llm llm.Engine, publisher *messaging.MessagePublisher, maxSteps int) *RepoAgent { var toolDefs []ToolDefinition if err := json.Unmarshal(ToolsJSON, &toolDefs); err != nil { slog.Error("Failed to unmarshal tools", "error", err) diff --git a/internal/agents/types.go b/internal/agents/types.go index 92564ce..3705b3e 100644 --- a/internal/agents/types.go +++ b/internal/agents/types.go @@ -3,10 +3,8 @@ package agents import ( "context" + "codeactor/internal/llm" "codeactor/pkg/messaging" - - "github.com/tmc/langchaingo/llms" - "github.com/tmc/langchaingo/tools" ) // Agent defines the interface for all agents in the system. @@ -17,7 +15,6 @@ type Agent interface { // BaseAgent holds common dependencies for agents. type BaseAgent struct { - LLM llms.LLM - Tools []tools.Tool + LLM llm.Engine Publisher *messaging.MessagePublisher } diff --git a/internal/app/app.go b/internal/app/app.go index be301bd..fcb45cd 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -15,13 +15,11 @@ import ( "codeactor/internal/llm" "codeactor/internal/memory" "codeactor/pkg/messaging" - - "github.com/tmc/langchaingo/llms" ) // CodingAssistant is the main entry point for the agent system. type CodingAssistant struct { - llm llms.LLM + engine llm.Engine config *config.Config conductor *agents.ConductorAgent dispatcher *messaging.MessageDispatcher @@ -39,15 +37,15 @@ func NewCodingAssistant(client *llm.Client) (*CodingAssistant, error) { ca := &CodingAssistant{ userResponseChannels: make(map[string]chan string), logger: slog.Default().With("component", "coding_assistant"), - llm: client.LLM, + engine: client.Engine, config: client.Config, } return ca, nil } -// Init initializes the assistant with LLM and creates agents. -func (ca *CodingAssistant) Init(llm llms.LLM, workDir string) { - ca.llm = llm +// Init initializes the assistant with Engine and creates agents. +func (ca *CodingAssistant) Init(engine llm.Engine, workDir string) { + ca.engine = engine // Initialize agents publisher := messaging.NewMessagePublisher(ca.dispatcher) @@ -69,7 +67,7 @@ func (ca *CodingAssistant) Init(llm llms.LLM, workDir string) { SysOps: tools.NewSystemOperationsTool(workDir), ReplaceTool: tools.NewReplaceBlockTool(workDir), ThinkingTool: tools.NewThinkingTool(), - MicroAgentTool: tools.NewMicroAgentTool(llm), + MicroAgentTool: tools.NewMicroAgentTool(engine), ImplPlanTool: tools.NewImplPlanTool(), FlowOps: tools.NewFlowControlTool(workDir), RepoOps: tools.NewRepoOperationsTool(fmt.Sprintf("http://127.0.0.1:%d", ca.CodebasePort), workDir), @@ -115,11 +113,11 @@ func (ca *CodingAssistant) Init(llm llms.LLM, workDir string) { // Parse disabled agents from comma-separated string disabledAgents := parseDisabledAgents(ca.DisabledAgents) - repoAgent := agents.NewRepoAgent(ca.globalCtx, llm, publisher, repoMaxSteps) - codingAgent := agents.NewCodingAgent(ca.globalCtx, llm, codingMaxSteps) - chatAgent := agents.NewChatAgent(ca.globalCtx, llm, chatMaxSteps) - metaAgent := agents.NewMetaAgent(ca.globalCtx, llm) - ca.conductor = agents.NewConductorAgent(ca.globalCtx, llm, repoAgent, codingAgent, chatAgent, metaAgent, conductorMaxSteps, disabledAgents, metaRetryCount) + repoAgent := agents.NewRepoAgent(ca.globalCtx, engine, publisher, repoMaxSteps) + codingAgent := agents.NewCodingAgent(ca.globalCtx, engine, codingMaxSteps) + chatAgent := agents.NewChatAgent(ca.globalCtx, engine, chatMaxSteps) + metaAgent := agents.NewMetaAgent(ca.globalCtx, engine) + ca.conductor = agents.NewConductorAgent(ca.globalCtx, engine, repoAgent, codingAgent, chatAgent, metaAgent, conductorMaxSteps, disabledAgents, metaRetryCount) } func (ca *CodingAssistant) IntegrateMessaging(dispatcher *messaging.MessageDispatcher) { @@ -177,14 +175,14 @@ func (r *TaskRequest) WithUserMessage(msg string) *TaskRequest { // ProcessCodingTaskWithCallback executes the task using the agent system. func (ca *CodingAssistant) ProcessCodingTaskWithCallback(req *TaskRequest) (string, error) { - ca.Init(ca.llm, req.projectDir) + ca.Init(ca.engine, req.projectDir) return ca.conductor.Run(req.ctx, req.taskDesc, req.memory) } // ProcessConversation handles chat messages. func (ca *CodingAssistant) ProcessConversation(req *TaskRequest) (string, error) { - ca.Init(ca.llm, req.projectDir) + ca.Init(ca.engine, req.projectDir) return ca.conductor.Run(req.ctx, req.userMessage, req.memory) } diff --git a/internal/llm/engine.go b/internal/llm/engine.go new file mode 100644 index 0000000..974dee0 --- /dev/null +++ b/internal/llm/engine.go @@ -0,0 +1,79 @@ +package llm + +import ( + "context" +) + +// Role represents the role of a message in a conversation. +type Role string + +const ( + RoleSystem Role = "system" + RoleUser Role = "user" + RoleAssistant Role = "assistant" + RoleTool Role = "tool" +) + +// Message represents a single message in a conversation. +type Message struct { + Role Role `json:"role"` + Content string `json:"content,omitempty"` + ToolCalls []ToolCall `json:"tool_calls,omitempty"` + ToolCallID string `json:"tool_call_id,omitempty"` + ToolName string `json:"name,omitempty"` + Reasoning string `json:"reasoning_content,omitempty"` // thinking/reasoning from models that support it +} + +// ToolDef defines a tool available to the LLM. +type ToolDef struct { + Type string `json:"type"` // "function" + Function FunctionDef `json:"function"` +} + +// FunctionDef defines a function tool's signature. +type FunctionDef struct { + Name string `json:"name"` + Description string `json:"description,omitempty"` + Parameters map[string]any `json:"parameters,omitempty"` +} + +// ToolCall represents a tool call from the LLM. +type ToolCall struct { + ID string `json:"id"` + Type string `json:"type"` // "function" + Function FunctionCall `json:"function"` +} + +// FunctionCall represents the function name and arguments in a tool call. +type FunctionCall struct { + Name string `json:"name"` + Arguments string `json:"arguments"` +} + +// Response represents the LLM's response to a GenerateContent call. +type Response struct { + Choices []Choice `json:"choices"` +} + +// Choice represents a single choice in the LLM response. +type Choice struct { + Content string `json:"content,omitempty"` + ToolCalls []ToolCall `json:"tool_calls,omitempty"` + Reasoning string `json:"reasoning_content,omitempty"` // thinking/reasoning content +} + +// CallOptions holds optional parameters for LLM calls. +type CallOptions struct { + MaxTokens int + Temperature float64 + StreamHandler StreamHandler +} + +// StreamHandler is called for each chunk during streaming. +type StreamHandler func(ctx context.Context, chunk []byte) error + +// Engine is the core LLM abstraction. +type Engine interface { + // GenerateContent sends messages and tools to the LLM and returns the response. + GenerateContent(ctx context.Context, messages []Message, tools []ToolDef, opts *CallOptions) (*Response, error) +} diff --git a/internal/llm/engine_openai.go b/internal/llm/engine_openai.go new file mode 100644 index 0000000..bec4184 --- /dev/null +++ b/internal/llm/engine_openai.go @@ -0,0 +1,259 @@ +package llm + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/openai/openai-go/v3" + "github.com/openai/openai-go/v3/option" + "github.com/openai/openai-go/v3/packages/param" + "github.com/openai/openai-go/v3/shared" +) + +// OpenAIEngine implements Engine using the official OpenAI Go SDK. +type OpenAIEngine struct { + client *openai.Client + model string +} + +// NewOpenAIEngine creates a new OpenAIEngine. +// baseURL is optional - if empty, uses OpenAI's default API endpoint. +func NewOpenAIEngine(baseURL, apiKey, model string) *OpenAIEngine { + opts := []option.RequestOption{ + option.WithAPIKey(apiKey), + } + if baseURL != "" { + opts = append(opts, option.WithBaseURL(baseURL)) + } + client := openai.NewClient(opts...) + return &OpenAIEngine{client: &client, model: model} +} + +// GenerateContent implements Engine. +func (e *OpenAIEngine) GenerateContent(ctx context.Context, messages []Message, tools []ToolDef, opts *CallOptions) (*Response, error) { + params := e.buildParams(messages, tools, opts) + + if opts != nil && opts.StreamHandler != nil { + return e.generateStreaming(ctx, params, opts.StreamHandler) + } + + completion, err := e.client.Chat.Completions.New(ctx, params) + if err != nil { + return nil, fmt.Errorf("openai chat completion: %w", err) + } + + return e.toResponse(completion), nil +} + +func (e *OpenAIEngine) buildParams(messages []Message, tools []ToolDef, opts *CallOptions) openai.ChatCompletionNewParams { + params := openai.ChatCompletionNewParams{ + Model: shared.ChatModel(e.model), + Messages: e.convertMessages(messages), + } + + if len(tools) > 0 { + params.Tools = e.convertTools(tools) + } + + if opts != nil { + if opts.MaxTokens > 0 { + params.MaxCompletionTokens = openai.Int(int64(opts.MaxTokens)) + } + if opts.Temperature > 0 { + params.Temperature = openai.Float(opts.Temperature) + } + } + + return params +} + +func (e *OpenAIEngine) generateStreaming(ctx context.Context, params openai.ChatCompletionNewParams, handler StreamHandler) (*Response, error) { + stream := e.client.Chat.Completions.NewStreaming(ctx, params) + defer stream.Close() + + var content string + var reasoning string + var toolCalls []ToolCall + + for stream.Next() { + chunk := stream.Current() + if len(chunk.Choices) > 0 { + delta := chunk.Choices[0].Delta + content += delta.Content + + // Extract reasoning_content from raw JSON if present (DeepSeek thinking mode) + if raw := delta.RawJSON(); raw != "" { + var rawDelta map[string]any + if err := json.Unmarshal([]byte(raw), &rawDelta); err == nil { + if rc, ok := rawDelta["reasoning_content"].(string); ok { + reasoning += rc + } + } + } + + // Accumulate tool call deltas + for _, tc := range delta.ToolCalls { + idx := int(tc.Index) + if idx >= len(toolCalls) { + // Extend slice if needed + for len(toolCalls) <= idx { + toolCalls = append(toolCalls, ToolCall{Type: "function"}) + } + } + + if tc.ID != "" { + toolCalls[idx].ID = tc.ID + } + if tc.Function.Name != "" { + toolCalls[idx].Function.Name = tc.Function.Name + } + toolCalls[idx].Function.Arguments += tc.Function.Arguments + } + + // Call handler with content chunk + if handler != nil && delta.Content != "" { + if err := handler(ctx, []byte(delta.Content)); err != nil { + return nil, err + } + } + } + } + + if err := stream.Err(); err != nil { + return nil, fmt.Errorf("openai streaming: %w", err) + } + + return &Response{ + Choices: []Choice{{ + Content: content, + Reasoning: reasoning, + ToolCalls: toolCalls, + }}, + }, nil +} + +func (e *OpenAIEngine) convertMessages(messages []Message) []openai.ChatCompletionMessageParamUnion { + result := make([]openai.ChatCompletionMessageParamUnion, 0, len(messages)) + for _, msg := range messages { + result = append(result, e.convertMessage(msg)) + } + return result +} + +func (e *OpenAIEngine) convertMessage(msg Message) openai.ChatCompletionMessageParamUnion { + switch msg.Role { + case RoleSystem: + return openai.SystemMessage(msg.Content) + case RoleUser: + return openai.UserMessage(msg.Content) + case RoleAssistant: + if len(msg.ToolCalls) > 0 { + return e.buildAssistantWithToolCalls(msg) + } + content := msg.Content + if msg.Reasoning != "" { + // DeepSeek thinking mode: reasoning_content must be echoed back. + // openai-go types don't expose reasoning_content, so inject via SetExtraFields. + assistant := &openai.ChatCompletionAssistantMessageParam{ + Content: openai.ChatCompletionAssistantMessageParamContentUnion{ + OfString: param.NewOpt(content), + }, + } + assistant.SetExtraFields(map[string]any{ + "reasoning_content": msg.Reasoning, + }) + return openai.ChatCompletionMessageParamUnion{ + OfAssistant: assistant, + } + } + return openai.AssistantMessage(content) + case RoleTool: + return openai.ToolMessage(msg.Content, msg.ToolCallID) + default: + return openai.UserMessage(msg.Content) + } +} + +func (e *OpenAIEngine) buildAssistantWithToolCalls(msg Message) openai.ChatCompletionMessageParamUnion { + toolCalls := make([]openai.ChatCompletionMessageToolCallUnionParam, 0, len(msg.ToolCalls)) + for _, tc := range msg.ToolCalls { + toolCalls = append(toolCalls, openai.ChatCompletionMessageToolCallUnionParam{ + OfFunction: &openai.ChatCompletionMessageFunctionToolCallParam{ + ID: tc.ID, + Type: "function", + Function: openai.ChatCompletionMessageFunctionToolCallFunctionParam{ + Name: tc.Function.Name, + Arguments: tc.Function.Arguments, + }, + }, + }) + } + + contentVal := msg.Content + assistant := &openai.ChatCompletionAssistantMessageParam{ + Content: openai.ChatCompletionAssistantMessageParamContentUnion{ + OfString: param.NewOpt(contentVal), + }, + ToolCalls: toolCalls, + } + if msg.Reasoning != "" { + assistant.SetExtraFields(map[string]any{ + "reasoning_content": msg.Reasoning, + }) + } + return openai.ChatCompletionMessageParamUnion{ + OfAssistant: assistant, + } +} + +func (e *OpenAIEngine) convertTools(tools []ToolDef) []openai.ChatCompletionToolUnionParam { + result := make([]openai.ChatCompletionToolUnionParam, 0, len(tools)) + for _, t := range tools { + if t.Type == "function" { + result = append(result, openai.ChatCompletionFunctionTool(shared.FunctionDefinitionParam{ + Name: t.Function.Name, + Description: openai.String(t.Function.Description), + Parameters: shared.FunctionParameters(t.Function.Parameters), + })) + } + } + return result +} + +func (e *OpenAIEngine) toResponse(completion *openai.ChatCompletion) *Response { + resp := &Response{} + for _, choice := range completion.Choices { + c := Choice{ + Content: choice.Message.Content, + } + + // Extract tool calls + for _, tc := range choice.Message.ToolCalls { + fn := tc.AsFunction() + if fn.ID != "" { + c.ToolCalls = append(c.ToolCalls, ToolCall{ + ID: fn.ID, + Type: "function", + Function: FunctionCall{ + Name: fn.Function.Name, + Arguments: fn.Function.Arguments, + }, + }) + } + } + + // Extract reasoning_content from raw JSON if present + if raw := choice.Message.RawJSON(); raw != "" { + var rawMsg map[string]any + if err := json.Unmarshal([]byte(raw), &rawMsg); err == nil { + if rc, ok := rawMsg["reasoning_content"].(string); ok { + c.Reasoning = rc + } + } + } + + resp.Choices = append(resp.Choices, c) + } + return resp +} diff --git a/internal/llm/llm.go b/internal/llm/llm.go index ae86a02..7b4ac1b 100644 --- a/internal/llm/llm.go +++ b/internal/llm/llm.go @@ -13,10 +13,6 @@ import ( "codeactor/internal/util" "log/slog" - - "github.com/tmc/langchaingo/llms" - "github.com/tmc/langchaingo/llms/bedrock" - "github.com/tmc/langchaingo/llms/openai" ) // llmLogger is a separate logger for LLM responses @@ -25,7 +21,6 @@ var llmLogFile *os.File // initLLMLogger initializes the LLM logger func initLLMLogger() error { - // Create logs directory in user home homeDir, err := os.UserHomeDir() if err != nil { return util.WrapError(context.Background(), err, "failed to get user home directory") @@ -36,7 +31,6 @@ func initLLMLogger() error { return util.WrapError(context.Background(), err, "failed to create logs directory") } - // Open LLM log file with date dateStr := time.Now().Format("2006-01-02") logFileName := fmt.Sprintf("llm-%s.log", dateStr) var errFile error @@ -45,7 +39,6 @@ func initLLMLogger() error { return util.WrapError(context.Background(), errFile, "failed to open LLM log file") } - // Create LLM logger with plain text format for better debugging handler := slog.NewTextHandler(llmLogFile, &slog.HandlerOptions{ Level: slog.LevelInfo, }) @@ -66,34 +59,23 @@ func LogLLMContent(title string, content string) { } } -// LoggingLLM wraps an llms.LLM to add logging -type LoggingLLM struct { - inner llms.LLM +// LoggingEngine wraps an Engine to add logging +type LoggingEngine struct { + inner Engine } -func (l *LoggingLLM) Call(ctx context.Context, prompt string, options ...llms.CallOption) (string, error) { - LogLLMContent("LLM Input (Call)", prompt) - resp, err := l.inner.Call(ctx, prompt, options...) - if err == nil { - LogLLMContent("LLM Response (Call)", resp) - } else { - LogLLMContent("LLM Error (Call)", err.Error()) - } - return resp, err -} - -func (l *LoggingLLM) GenerateContent(ctx context.Context, messages []llms.MessageContent, options ...llms.CallOption) (*llms.ContentResponse, error) { +func (l *LoggingEngine) GenerateContent(ctx context.Context, messages []Message, tools []ToolDef, opts *CallOptions) (*Response, error) { if msgsJSON, err := json.MarshalIndent(messages, "", " "); err == nil { - LogLLMContent("LLM Input (GenerateContent)", string(msgsJSON)) - } else { - LogLLMContent("LLM Input (GenerateContent) - JSON Error", fmt.Sprintf("Failed to marshal messages: %v", err)) + LogLLMContent("LLM Input (messages)", string(msgsJSON)) + } + if toolsJSON, err := json.MarshalIndent(tools, "", " "); err == nil { + LogLLMContent("LLM Input (tools)", string(toolsJSON)) } - resp, err := l.inner.GenerateContent(ctx, messages, options...) + resp, err := l.inner.GenerateContent(ctx, messages, tools, opts) if err == nil && len(resp.Choices) > 0 { choice := resp.Choices[0] logContent := choice.Content - if len(choice.ToolCalls) > 0 { var toolCallsLog strings.Builder if logContent != "" { @@ -101,25 +83,24 @@ func (l *LoggingLLM) GenerateContent(ctx context.Context, messages []llms.Messag } toolCallsLog.WriteString("[Tool Calls]:\n") for i, tc := range choice.ToolCalls { - toolCallsLog.WriteString(fmt.Sprintf("%d. %s(%s)\n", i+1, tc.FunctionCall.Name, tc.FunctionCall.Arguments)) + toolCallsLog.WriteString(fmt.Sprintf("%d. %s(%s)\n", i+1, tc.Function.Name, tc.Function.Arguments)) } logContent += toolCallsLog.String() } - - LogLLMContent("LLM Response (GenerateContent)", logContent) + LogLLMContent("LLM Response", logContent) } else if err != nil { - LogLLMContent("LLM Error (GenerateContent)", err.Error()) + LogLLMContent("LLM Error", err.Error()) } return resp, err } // Client represents an LLM client type Client struct { - LLM llms.LLM + Engine Engine Config *config.Config } -// LoadConfig loads configuration from a TOML file using the new multi-provider structure +// LoadConfig loads configuration from a TOML file using the multi-provider structure func LoadConfig(configPath string) (*config.Config, error) { slog.Debug("Decoding TOML configuration file", "config_path", configPath) @@ -131,7 +112,6 @@ func LoadConfig(configPath string) (*config.Config, error) { return nil, err } - // Get active provider for logging activeProvider, err := config.GetActiveProvider() if err != nil { slog.Error("Failed to get active provider configuration", "error", err) @@ -153,78 +133,35 @@ func LoadConfig(configPath string) (*config.Config, error) { func NewClient(config *config.Config) (*Client, error) { ctx := context.Background() - // Initialize LLM logger if err := initLLMLogger(); err != nil { slog.Error("Failed to initialize LLM logger", "error", err) return nil, util.WrapError(ctx, err, "failed to initialize LLM logger") } - // Get active provider configuration activeProvider, err := config.GetActiveProvider() if err != nil { slog.Error("Failed to get active provider configuration", "error", err) return nil, util.WrapError(ctx, err, "failed to get active provider") } + if activeProvider.APIKey == "" { + slog.Error("Cannot create LLM client: API key is empty") + return nil, util.WrapError(ctx, fmt.Errorf("API key not found in config"), "API key validation failed") + } + slog.Info("Creating new LLM client", "provider", config.LLM.UseProvider, "model", activeProvider.Model, "api_base_url", activeProvider.APIBaseURL) - var llm llms.LLM - - // Handle Bedrock provider separately - if config.LLM.UseProvider == "bedrock" { - // Detect provider from model ID for Bedrock - modelProvider := activeProvider.ModelProvider - - // Create Bedrock client - bedrockOpts := []bedrock.Option{ - bedrock.WithModel(activeProvider.Model), - } - if modelProvider != "" { - bedrockOpts = append(bedrockOpts, bedrock.WithModelProvider(modelProvider)) - } - - llm, err = bedrock.New(bedrockOpts...) - if err != nil { - slog.Error("Failed to create Bedrock LLM client", - "error", err, - "provider", config.LLM.UseProvider, - "model", activeProvider.Model, - "model_provider", modelProvider) - return nil, util.WrapError(ctx, err, "failed to create Bedrock client") - } - } else { - // For non-Bedrock providers, require API key - if activeProvider.APIKey == "" { - slog.Error("Cannot create LLM client: API key is empty") - return nil, util.WrapError(ctx, fmt.Errorf("API key not found in config"), "API key validation failed") - } - - // Create client using OpenAI's client but with specified API endpoint - llm, err = openai.New( - openai.WithModel(activeProvider.Model), - openai.WithBaseURL(activeProvider.APIBaseURL), - openai.WithToken(activeProvider.APIKey), - ) - if err != nil { - slog.Error("Failed to create LLM client", - "error", err, - "provider", config.LLM.UseProvider, - "model", activeProvider.Model, - "api_base_url", activeProvider.APIBaseURL) - return nil, util.WrapError(ctx, err, "failed to create OpenAI client") - } - } + engine := NewOpenAIEngine(activeProvider.APIBaseURL, activeProvider.APIKey, activeProvider.Model) slog.Info("LLM client created successfully") - // Wrap with LoggingLLM - loggingLLM := &LoggingLLM{inner: llm} + loggingEngine := &LoggingEngine{inner: engine} return &Client{ - LLM: loggingLLM, + Engine: loggingEngine, Config: config, }, nil } @@ -240,12 +177,11 @@ func StreamDebugHandler(ctx context.Context, chunk []byte) error { } // GenerateCompletionWithMemory generates a completion using the provided memory (conversation history) -func (c *Client) GenerateCompletionWithMemory(ctx context.Context, memory []llms.MessageContent, streamHandler func(context.Context, []byte) error) (string, error) { +func (c *Client) GenerateCompletionWithMemory(ctx context.Context, memory []Message, streamHandler func(context.Context, []byte) error) (string, error) { slog.Debug("Starting completion generation with memory", "memory_length", len(memory), "streaming_enabled", c.Config.App.EnableStreaming) - // Log input memory to LLM log file if memoryJSON, err := json.Marshal(memory); err == nil { llmLogger.Info("LLM input memory", "type", "input_memory", @@ -254,36 +190,24 @@ func (c *Client) GenerateCompletionWithMemory(ctx context.Context, memory []llms "memory", string(memoryJSON)) } - // Log LLM input using assistant's logger if available - /* - if c.assistant != nil && c.assistant.logger != nil { - c.assistant.logger.LogLLMInput("", memory, nil) - } - */ - - // Get active provider configuration activeProvider, err := c.Config.GetActiveProvider() if err != nil { slog.Error("Failed to get active provider configuration", "error", err) return "", util.WrapError(ctx, err, "failed to get active provider") } - // Generate options - opts := []llms.CallOption{ - llms.WithMaxTokens(activeProvider.MaxTokens), - llms.WithTemperature(activeProvider.Temperature), + opts := &CallOptions{ + MaxTokens: activeProvider.MaxTokens, + Temperature: activeProvider.Temperature, } - // Add streaming if enabled and handler provided if c.Config.App.EnableStreaming && streamHandler != nil { - opts = append(opts, llms.WithStreamingFunc(streamHandler)) + opts.StreamHandler = streamHandler slog.Debug("Streaming enabled for this request (memory)") } - // Generate completion - completion, err := c.LLM.GenerateContent(ctx, memory, opts...) + completion, err := c.Engine.GenerateContent(ctx, memory, nil, opts) if err != nil { - // 尝试提取HTTP响应内容 httpResponse := extractHTTPResponse(err) slog.Error("Failed to GenerateContent", @@ -292,7 +216,6 @@ func (c *Client) GenerateCompletionWithMemory(ctx context.Context, memory []llms "memory_length", len(memory), "http_response", httpResponse) - // Log error to LLM log file llmLogger.Error("LLM completion error", "type", "completion_error", "model", c.Config.LLM.UseProvider, @@ -303,14 +226,12 @@ func (c *Client) GenerateCompletionWithMemory(ctx context.Context, memory []llms return "", util.WrapError(ctx, err, "error generating completion (memory)") } - // Return result if len(completion.Choices) > 0 { result := completion.Choices[0].Content slog.Info("Completion generated successfully (memory)", "result_length", len(result), "choices_count", len(completion.Choices)) - // Log the complete response to LLM log file if choicesJSON, err := json.Marshal(completion.Choices); err == nil { llmLogger.Info("LLM completion output", "type", "completion_output", @@ -321,20 +242,12 @@ func (c *Client) GenerateCompletionWithMemory(ctx context.Context, memory []llms "response.Choices", string(choicesJSON)) } - // Log LLM output using assistant's logger if available - /* - if c.assistant != nil && c.assistant.logger != nil { - c.assistant.logger.LogLLMOutput("", completion) - } - */ - return result, nil } slog.Warn("No completion choices returned from LLM (memory)", "choices_count", len(completion.Choices)) - // Log empty response to LLM log file llmLogger.Warn("LLM returned empty completion (with memory)", "type", "empty_completion_memory", "model", c.Config.LLM.UseProvider, @@ -350,31 +263,5 @@ func extractHTTPResponse(err error) string { return "" } - // 将错误转换为字符串 - errStr := err.Error() - - // 检查AWS Bedrock特定的错误 - if strings.Contains(errStr, "ValidationException") { - return fmt.Sprintf("Bedrock Validation Error: %s", errStr) - } - - if strings.Contains(errStr, "ThrottlingException") { - return fmt.Sprintf("Bedrock Throttling Error: %s", errStr) - } - - if strings.Contains(errStr, "AccessDeniedException") { - return fmt.Sprintf("Bedrock Access Denied: %s", errStr) - } - - if strings.Contains(errStr, "ModelNotReadyException") { - return fmt.Sprintf("Bedrock Model Not Ready: %s", errStr) - } - - // 检查其他常见的HTTP错误格式 - if strings.Contains(errStr, "status code") || strings.Contains(errStr, "HTTP") { - return errStr - } - - // 如果没有明显的HTTP错误信息,返回原始错误 - return fmt.Sprintf("Error: %s", errStr) + return fmt.Sprintf("Error: %s", err.Error()) } diff --git a/internal/tools/adapter.go b/internal/tools/adapter.go index 1c80a0d..f61c54b 100644 --- a/internal/tools/adapter.go +++ b/internal/tools/adapter.go @@ -5,13 +5,13 @@ import ( "encoding/json" "fmt" - "github.com/tmc/langchaingo/llms" + "codeactor/internal/llm" ) // ToolFunc is a function type that matches the tool execution signature type ToolFunc func(ctx context.Context, params map[string]interface{}) (interface{}, error) -// Adapter wraps a function to implement the langchaingo tools.Tool interface +// Adapter wraps a ToolFunc with name, description, and schema for LLM function calling type Adapter struct { name string description string @@ -97,11 +97,11 @@ func SetGuardOnAdapters(adapters []*Adapter, guard *WorkspaceGuard) { } } -// ToLLMSTool converts the adapter to an llms.Tool definition -func (a *Adapter) ToLLMSTool() llms.Tool { - return llms.Tool{ +// ToToolDef converts the adapter to an llm.ToolDef definition +func (a *Adapter) ToToolDef() llm.ToolDef { + return llm.ToolDef{ Type: "function", - Function: &llms.FunctionDefinition{ + Function: llm.FunctionDef{ Name: a.name, Description: a.description, Parameters: a.schema, diff --git a/internal/tools/micro_agent.go b/internal/tools/micro_agent.go index a316517..884e8c5 100644 --- a/internal/tools/micro_agent.go +++ b/internal/tools/micro_agent.go @@ -4,18 +4,18 @@ import ( "context" "fmt" - "github.com/tmc/langchaingo/llms" + "codeactor/internal/llm" ) // MicroAgentTool enables agents to make raw LLM inference calls as a tool. // ChatAgent and CodingAgent use this when they need a sub-LLM reasoning step // with a custom system prompt, independent of the current conversation context. type MicroAgentTool struct { - LLM llms.LLM + LLM llm.Engine } // NewMicroAgentTool creates a new MicroAgentTool with the given LLM client. -func NewMicroAgentTool(llm llms.LLM) *MicroAgentTool { +func NewMicroAgentTool(llm llm.Engine) *MicroAgentTool { return &MicroAgentTool{LLM: llm} } @@ -32,18 +32,18 @@ func (t *MicroAgentTool) Execute(ctx context.Context, params map[string]interfac return nil, fmt.Errorf("task parameter is required and must be a non-empty string") } - messages := []llms.MessageContent{ + messages := []llm.Message{ { - Role: llms.ChatMessageTypeSystem, - Parts: []llms.ContentPart{llms.TextPart(systemPrompt)}, + Role: llm.RoleSystem, + Content: systemPrompt, }, { - Role: llms.ChatMessageTypeHuman, - Parts: []llms.ContentPart{llms.TextPart(task)}, + Role: llm.RoleUser, + Content: task, }, } - resp, err := t.LLM.GenerateContent(ctx, messages) + resp, err := t.LLM.GenerateContent(ctx, messages, nil, nil) if err != nil { return nil, fmt.Errorf("micro_agent LLM call failed: %w", err) }