Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tools for assistant to place orders and view orders #96

Merged
merged 10 commits into from
Mar 5, 2025
6 changes: 5 additions & 1 deletion app/assistant/cli/ai/driver/eino/request_props.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ func RebuildTools(caller model.GetServerCaller) {
// BuildTools builds the tools
func BuildTools(strategy model.GetServerCaller) []tool.BaseTool {
return []tool.BaseTool{
remote.Ping(strategy),
// remote.Ping(strategy),
remote.CartShow(strategy),
remote.CartPurchase(strategy),
remote.OrderList(strategy),
remote.OrderView(strategy),
}
}
107 changes: 107 additions & 0 deletions app/assistant/cli/ai/driver/eino/tools/remote/cart_purchase.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
Copyright 2024 The west2-online Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remote

import (
"context"

"github.com/bytedance/sonic"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/schema"

strategy "github.com/west2-online/DomTok/app/assistant/cli/ai/driver/eino/model"
"github.com/west2-online/DomTok/app/assistant/cli/ai/driver/eino/tools"
"github.com/west2-online/DomTok/app/gateway/model/api/cart"
"github.com/west2-online/DomTok/app/gateway/model/model"
"github.com/west2-online/DomTok/pkg/errno"
)

const (
ToolCartPurchaseName = "cart_purchase"
ToolCartPurchaseDesc = "当用户想要下单时,可以使用此工具。从购物车中购买商品,需要购物车中的商品信息(调用Tool: show_cart)"
)

type ToolCartPurchaseArgs struct {
BaseOrderGoods []_CartPurchaseBaseOrderGoods `json:"base_order_goods" desc:"填入订单商品信息" required:"true"`
}

type _CartPurchaseBaseOrderGoods struct {
MerchantID int64 `json:"merchant_id" desc:"填入商家ID" required:"true"`
GoodsID int64 `json:"goods_id" desc:"填入商品ID" required:"true"`
SkuID int64 `json:"sku_id" desc:"填入SKU ID" required:"true"`
GoodsVersion int64 `json:"goods_version" desc:"填入商品版本" required:"true"`
PurchaseQuantity int64 `json:"purchase_quantity" desc:"填入购买数量" required:"true"`
}

var ToolCartPurchaseRequestBody = schema.NewParamsOneOfByParams(*tools.Reflect(ToolCartPurchaseArgs{}))

type ToolCartPurchase struct {
tool.InvokableTool

getServerCaller strategy.GetServerCaller
}

func CartPurchase(strategy strategy.GetServerCaller) *ToolCartPurchase {
return &ToolCartPurchase{
getServerCaller: strategy,
}
}

func (t *ToolCartPurchase) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
s := t.getServerCaller(ToolCartPurchaseName)
if s == nil {
return "", errno.NewErrNoWithStack(errno.InternalServiceErrorCode, "server is nil")
}
params := &ToolCartPurchaseArgs{}
if err := sonic.Unmarshal([]byte(argumentsInJSON), params); err != nil {
return "", errno.NewErrNoWithStack(errno.InternalServiceErrorCode, "unmarshal arguments failed")
}
resp, err := s.CartPurchase(ctx, &cart.PurChaseCartGoodsRequest{
CartGoods: ConvertArgsOrderGoodsToRequestGoods(params.BaseOrderGoods...),
})
if err != nil {
return err.Error(), nil //nolint: nilerr
}

return string(resp), nil
}

func (t *ToolCartPurchase) Info(_ context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: ToolCartPurchaseName,
Desc: ToolCartPurchaseDesc,
ParamsOneOf: ToolCartPurchaseRequestBody,
}, nil
}

func (b *_CartPurchaseBaseOrderGoods) ToRequestGoods() *model.CartGoods {
return &model.CartGoods{
MerchantId: b.MerchantID,
GoodsId: b.GoodsID,
SkuId: b.SkuID,
GoodsVersion: b.GoodsVersion,
PurchaseQuantity: b.PurchaseQuantity,
}
}

func ConvertArgsOrderGoodsToRequestGoods(gs ..._CartPurchaseBaseOrderGoods) []*model.CartGoods {
res := make([]*model.CartGoods, len(gs))
for i, g := range gs {
res[i] = g.ToRequestGoods()
}
return res
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copyright 2024 The west2-online Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remote

import (
"context"
"errors"
"testing"

. "github.com/bytedance/mockey"
"github.com/bytedance/sonic"
. "github.com/smartystreets/goconvey/convey"

"github.com/west2-online/DomTok/app/assistant/cli/server/adapter"
"github.com/west2-online/DomTok/app/gateway/model/api/cart"
)

func TestToolCartPurchase_InvokableRun(t *testing.T) {
f := CartPurchase(nil)
type MockServerCaller struct {
adapter.ServerCaller
}
fakeServerCaller := &MockServerCaller{}
args := ToolCartPurchaseArgs{
BaseOrderGoods: []_CartPurchaseBaseOrderGoods{{
MerchantID: 1,
GoodsID: 2,
SkuID: 3,
}},
}
argsBytes, _ := sonic.Marshal(args)
PatchConvey("Test OrderCreate", t, func() {
PatchConvey("success", func() {
mp := map[string]interface{}{}
MockValue(&f.getServerCaller).To(func(_ string) adapter.ServerCaller { return fakeServerCaller })
Mock((*MockServerCaller).CartPurchase).To(func(_ context.Context, params *cart.PurChaseCartGoodsRequest) ([]byte, error) {
mp["base_order_goods"] = params.CartGoods
return nil, nil
}).Build()

_, err := f.InvokableRun(context.Background(), string(argsBytes))
So(err, ShouldBeNil)
So(mp["base_order_goods"], ShouldResemble, ConvertArgsOrderGoodsToRequestGoods(args.BaseOrderGoods...))
})

PatchConvey("if server caller is nil", func() {
MockValue(&f.getServerCaller).To(func(_ string) adapter.ServerCaller { return nil })
Mock((*MockServerCaller).CartPurchase).Return([]byte("pong"), nil).Build()

_, err := f.InvokableRun(context.Background(), string(argsBytes))

So(err, ShouldNotBeNil)
})

PatchConvey("if server caller returns error", func() {
MockValue(&f.getServerCaller).To(func(_ string) adapter.ServerCaller { return fakeServerCaller })
Mock((*MockServerCaller).CartPurchase).Return(nil, errors.New("dial error")).Build()

resp, err := f.InvokableRun(context.Background(), string(argsBytes))

So(err, ShouldBeNil)
So(resp, ShouldEqual, "dial error")
})
})
}
78 changes: 78 additions & 0 deletions app/assistant/cli/ai/driver/eino/tools/remote/cart_show.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
Copyright 2024 The west2-online Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remote

import (
"context"

"github.com/bytedance/sonic"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/schema"

strategy "github.com/west2-online/DomTok/app/assistant/cli/ai/driver/eino/model"
"github.com/west2-online/DomTok/app/assistant/cli/ai/driver/eino/tools"
"github.com/west2-online/DomTok/app/gateway/model/api/cart"
"github.com/west2-online/DomTok/pkg/errno"
)

const (
ToolCartShowName = "show_cart"
ToolCartShowDesc = "当用户想查看购物车或从购物车下单时,可以使用此工具"
)

type ToolCartShowArgs struct {
PageNum int64 `json:"page_num" desc:"填入页码" required:"true"`
}

var ToolCartShowRequestBody = schema.NewParamsOneOfByParams(*tools.Reflect(ToolCartShowArgs{}))

type ToolCartShow struct {
tool.InvokableTool

getServerCaller strategy.GetServerCaller
}

func CartShow(strategy strategy.GetServerCaller) *ToolCartShow {
return &ToolCartShow{
getServerCaller: strategy,
}
}

func (t *ToolCartShow) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
s := t.getServerCaller(ToolPingName)
if s == nil {
return "", errno.NewErrNoWithStack(errno.InternalServiceErrorCode, "server is nil")
}
params := &ToolCartShowArgs{}
if err := sonic.Unmarshal([]byte(argumentsInJSON), params); err != nil {
return "", errno.NewErrNoWithStack(errno.InternalServiceErrorCode, "unmarshal arguments failed")
}
resp, err := s.CartShow(ctx, &cart.ShowCartGoodsListRequest{PageNum: params.PageNum})
if err != nil {
return err.Error(), nil //nolint: nilerr
}

return string(resp), nil
}

func (t *ToolCartShow) Info(_ context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: ToolCartShowName,
Desc: ToolCartShowDesc,
ParamsOneOf: ToolCartShowRequestBody,
}, nil
}
72 changes: 72 additions & 0 deletions app/assistant/cli/ai/driver/eino/tools/remote/cart_show_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright 2024 The west2-online Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package remote

import (
"context"
"errors"
"fmt"
"testing"

. "github.com/bytedance/mockey"
"github.com/bytedance/sonic"
. "github.com/smartystreets/goconvey/convey"

"github.com/west2-online/DomTok/app/assistant/cli/server/adapter"
"github.com/west2-online/DomTok/app/gateway/model/api/cart"
)

func TestToolCartShow_InvokableRun(t *testing.T) {
f := CartShow(nil)
type MockServerCaller struct {
adapter.ServerCaller
}
fakeServerCaller := &MockServerCaller{}
args := ToolCartShowArgs{PageNum: 4}
argsBytes, _ := sonic.Marshal(args)
PatchConvey("Test ToolCartShow", t, func() {
PatchConvey("success", func() {
MockValue(&f.getServerCaller).To(func(_ string) adapter.ServerCaller { return fakeServerCaller })
Mock((*MockServerCaller).CartShow).To(func(_ context.Context, params *cart.ShowCartGoodsListRequest) ([]byte, error) {
return []byte(fmt.Sprintf("page_num: %d", params.PageNum)), nil
}).Build()

resp, err := f.InvokableRun(context.Background(), string(argsBytes))
So(err, ShouldBeNil)
So(resp, ShouldEqual, "page_num: 4")
})

PatchConvey("if server caller is nil", func() {
MockValue(&f.getServerCaller).To(func(_ string) adapter.ServerCaller { return nil })
Mock((*MockServerCaller).CartShow).Return([]byte("pong"), nil).Build()

_, err := f.InvokableRun(context.Background(), string(argsBytes))

So(err, ShouldNotBeNil)
})

PatchConvey("if server caller returns error", func() {
MockValue(&f.getServerCaller).To(func(_ string) adapter.ServerCaller { return fakeServerCaller })
Mock((*MockServerCaller).CartShow).Return(nil, errors.New("dial error")).Build()

resp, err := f.InvokableRun(context.Background(), string(argsBytes))

So(err, ShouldBeNil)
So(resp, ShouldEqual, "dial error")
})
})
}
Loading