Skip to content

Commit

Permalink
岗位分析和ai通用接口添加 (#278)
Browse files Browse the repository at this point in the history
Co-authored-by: zhuwenliang <[email protected]>
  • Loading branch information
juniaoshaonian and zhuwenliang authored Nov 23, 2024
1 parent d1dc7ae commit c2d75ee
Show file tree
Hide file tree
Showing 15 changed files with 559 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .script/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ test -x $TARGET_PUSH || chmod +x $TARGET_PUSH
test -x $TARGET_COMMIT || chmod +x $TARGET_COMMIT

echo "安装 golangci-lint..."
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0

echo "安装 goimports..."
go install golang.org/x/tools/cmd/goimports@latest
19 changes: 19 additions & 0 deletions internal/ai/internal/domain/jd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package domain

const (
AnalysisJDTech = "analysis_jd_tech"
AnalysisJDBiz = "analysis_jd_biz"
AnalysisJDPosition = "analysis_jd_position"
)

type JDEvaluation struct {
Score int `json:"score"`
Analysis string `json:"analysis"`
}

type JD struct {
Amount int64
TechScore *JDEvaluation
BizScore *JDEvaluation
PosScore *JDEvaluation
}
10 changes: 10 additions & 0 deletions internal/ai/internal/errs/code.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package errs

var (
SystemError = ErrorCode{Code: 516001, Msg: "系统错误"}
)

type ErrorCode struct {
Code int
Msg string
}
264 changes: 264 additions & 0 deletions internal/ai/internal/integration/llm_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,18 @@ package integration
import (
"context"
"errors"
"net/http"
"testing"
"time"

"github.com/ecodeclub/ekit/iox"
"github.com/ecodeclub/ginx/session"
"github.com/ecodeclub/webook/internal/ai/internal/web"
"github.com/ecodeclub/webook/internal/test"
"github.com/gin-gonic/gin"
"github.com/gotomicro/ego/core/econf"
"github.com/gotomicro/ego/server/egin"

"github.com/ecodeclub/ekit/sqlx"
"github.com/ecodeclub/webook/internal/ai/internal/service/llm"
hdlmocks "github.com/ecodeclub/webook/internal/ai/internal/service/llm/handler/mocks"
Expand Down Expand Up @@ -65,6 +74,37 @@ func (s *LLMServiceSuite) SetupSuite() {
Utime: now,
}).Error
s.NoError(err)

err = s.db.Create(&dao.BizConfig{
Biz: domain.AnalysisJDBiz,
MaxInput: 100,
PromptTemplate: "这是岗位描述 %s",
KnowledgeId: knowledgeId,
Ctime: now,
Utime: now,
}).Error
s.NoError(err)

err = s.db.Create(&dao.BizConfig{
Biz: domain.AnalysisJDTech,
MaxInput: 100,
PromptTemplate: "这是岗位描述tech %s",
KnowledgeId: knowledgeId,
Ctime: now,
Utime: now,
}).Error
s.NoError(err)

err = s.db.Create(&dao.BizConfig{
Biz: domain.AnalysisJDPosition,
MaxInput: 100,
PromptTemplate: "这是岗位描述position %s",
KnowledgeId: knowledgeId,
Ctime: now,
Utime: now,
}).Error
s.NoError(err)

}

func (s *LLMServiceSuite) TearDownSuite() {
Expand Down Expand Up @@ -461,6 +501,230 @@ func (s *LLMServiceSuite) TestService() {
}
}

func (s *LLMServiceSuite) TestHandler_Ask() {
testCases := []struct {
name string
req web.LLMRequest
before func(t *testing.T, ctrl *gomock.Controller) (*hdlmocks.MockHandler, credit.Service)
assertFunc assert.ErrorAssertionFunc
after func(t *testing.T, resp web.LLMResponse)
wantCode int
}{
{
name: "八股文web-成功",
req: web.LLMRequest{
Biz: domain.BizQuestionExamine,
Input: []string{
"问题1",
"问题1内容",
"用户输入1",
},
},
assertFunc: assert.NoError,
before: func(t *testing.T,
ctrl *gomock.Controller) (*hdlmocks.MockHandler, credit.Service) {
llmHdl := hdlmocks.NewMockHandler(ctrl)
llmHdl.EXPECT().Handle(gomock.Any(), gomock.Any()).
Return(domain.LLMResponse{
Tokens: 100,
Amount: 100,
Answer: "aians",
}, nil)
creditSvc := creditmocks.NewMockService(ctrl)
creditSvc.EXPECT().GetCreditsByUID(gomock.Any(), gomock.Any()).Return(credit.Credit{
TotalAmount: 1000,
}, nil)
creditSvc.EXPECT().TryDeductCredits(gomock.Any(), gomock.Any()).Return(11, nil)
creditSvc.EXPECT().ConfirmDeductCredits(gomock.Any(), int64(123), int64(11)).Return(nil)
return llmHdl, creditSvc
},
after: func(t *testing.T, resp web.LLMResponse) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
// 校验response写入的内容是否正确
assert.Equal(t, web.LLMResponse{
Amount: 100,
RawResult: "aians",
}, resp)

var logModel dao.LLMRecord
err := s.db.WithContext(ctx).Where("id = ?", 1).First(&logModel).Error
require.NoError(t, err)
assert.True(t, logModel.Tid != "")
s.assertLog(dao.LLMRecord{
Id: 1,
Uid: 123,
Tid: logModel.Tid,
Biz: domain.BizQuestionExamine,
Tokens: 100,
Amount: 100,
KnowledgeId: knowledgeId,
Input: sqlx.JsonColumn[[]string]{
Valid: true,
Val: []string{
"问题1",
"问题1内容",
"用户输入1",
},
},
Status: 1,
PromptTemplate: sqlx.NewNullString("这是问题 %s,这是问题内容 %s,这是用户输入 %s"),
Answer: sqlx.NewNullString("aians"),
}, logModel)
// 校验credit写入的内容是否正确
var creditLogModel dao.LLMCredit
err = s.db.WithContext(ctx).Where("id = ?", 1).First(&creditLogModel).Error
require.NoError(t, err)
assert.True(t, logModel.Tid != "")

s.assertCreditLog(dao.LLMCredit{
Id: 1,
Tid: logModel.Tid,
Uid: 123,
Biz: domain.BizQuestionExamine,
Amount: 100,
Status: 1,
}, creditLogModel)
},
wantCode: 200,
},
}
for _, tc := range testCases {
tc := tc
s.T().Run(tc.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockHdl, mockCredit := tc.before(t, ctrl)
mou, err := startup.InitModule(s.db, mockHdl, &credit.Module{Svc: mockCredit})
require.NoError(t, err)
req, err := http.NewRequest(http.MethodPost,
"/ai/ask", iox.NewJSONReader(tc.req))
req.Header.Set("content-type", "application/json")
require.NoError(t, err)
econf.Set("server", map[string]any{"contextTimeout": "1s"})

server := egin.Load("server").Build()
server.Use(func(ctx *gin.Context) {
ctx.Set(session.CtxSessionKey,
session.NewMemorySession(session.Claims{
Uid: 123,
}))
})
mou.Hdl.PrivateRoutes(server.Engine)
recorder := test.NewJSONResponseRecorder[web.LLMResponse]()
server.ServeHTTP(recorder, req)
require.Equal(t, tc.wantCode, recorder.Code)
tc.after(t, recorder.MustScan().Data)
})
}
}

func (s *LLMServiceSuite) TestHandler_AnalysisJD() {
testCases := []struct {
name string
req web.JDRequest
before func(t *testing.T, ctrl *gomock.Controller) (*hdlmocks.MockHandler, credit.Service)
assertFunc assert.ErrorAssertionFunc
after func(t *testing.T, resp web.JDResponse)
wantCode int
}{
{
name: "岗位测评",
req: web.JDRequest{
JD: "我们招聘一个go工程师",
},
assertFunc: assert.NoError,
before: func(t *testing.T,
ctrl *gomock.Controller) (*hdlmocks.MockHandler, credit.Service) {
llmHdl := hdlmocks.NewMockHandler(ctrl)
llmHdl.EXPECT().Handle(gomock.Any(), gomock.Any()).
DoAndReturn(func(ctx context.Context, request domain.LLMRequest) (domain.LLMResponse, error) {
if request.Biz == "analysis_jd_tech" {
return domain.LLMResponse{
Tokens: 1000,
Amount: 100,
Answer: `{"score": 7, "analysis": "tech"}`,
}, nil
}
if request.Biz == "analysis_jd_biz" {
return domain.LLMResponse{
Tokens: 100,
Amount: 200,
Answer: `{"score": 8, "analysis": "biz"}`,
}, nil
}
if request.Biz == "analysis_jd_position" {
return domain.LLMResponse{
Tokens: 100,
Amount: 100,
Answer: `{"score": 5, "analysis": "position"}`,
}, nil
}
return domain.LLMResponse{}, errors.New("unknown biz")
}).AnyTimes()
creditSvc := creditmocks.NewMockService(ctrl)
creditSvc.EXPECT().GetCreditsByUID(gomock.Any(), gomock.Any()).Return(credit.Credit{
TotalAmount: 200000,
}, nil).AnyTimes()
creditSvc.EXPECT().TryDeductCredits(gomock.Any(), gomock.Any()).Return(11, nil).AnyTimes()
creditSvc.EXPECT().ConfirmDeductCredits(gomock.Any(), int64(123), int64(11)).Return(nil).AnyTimes()
return llmHdl, creditSvc
},
after: func(t *testing.T, resp web.JDResponse) {
// 校验response写入的内容是否正确
assert.Equal(t, web.JDResponse{
Amount: 400,
TechScore: &web.JDEvaluation{
Score: 7,
Analysis: "tech",
},
BizScore: &web.JDEvaluation{
Score: 8,
Analysis: "biz",
},
PosScore: &web.JDEvaluation{
Score: 5,
Analysis: "position",
},
}, resp)

},
wantCode: 200,
},
}
for _, tc := range testCases {
tc := tc
s.T().Run(tc.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockHdl, mockCredit := tc.before(t, ctrl)
mou, err := startup.InitModule(s.db, mockHdl, &credit.Module{Svc: mockCredit})
require.NoError(t, err)
req, err := http.NewRequest(http.MethodPost,
"/ai/analysis_jd", iox.NewJSONReader(tc.req))
req.Header.Set("content-type", "application/json")
require.NoError(t, err)
econf.Set("server", map[string]any{"contextTimeout": "1s"})
server := egin.Load("server").Build()
server.Use(func(ctx *gin.Context) {
ctx.Set(session.CtxSessionKey,
session.NewMemorySession(session.Claims{
Uid: 123,
}))
})
mou.Hdl.PrivateRoutes(server.Engine)
recorder := test.NewJSONResponseRecorder[web.JDResponse]()
server.ServeHTTP(recorder, req)
require.Equal(t, tc.wantCode, recorder.Code)
tc.after(t, recorder.MustScan().Data)
err = s.db.Exec("TRUNCATE TABLE `llm_records`").Error
require.NoError(s.T(), err)
err = s.db.Exec("TRUNCATE TABLE `llm_credits`").Error
require.NoError(s.T(), err)
})
}
}

func (s *LLMServiceSuite) assertLog(wantLog dao.LLMRecord, actual dao.LLMRecord) {
require.True(s.T(), actual.Ctime != 0)
require.True(s.T(), actual.Utime != 0)
Expand Down
7 changes: 6 additions & 1 deletion internal/ai/internal/integration/startup/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ package startup
import (
"sync"

"github.com/ecodeclub/webook/internal/ai/internal/service"
"github.com/ecodeclub/webook/internal/ai/internal/web"

hdlmocks "github.com/ecodeclub/webook/internal/ai/internal/service/llm/handler/mocks"

"github.com/ecodeclub/webook/internal/ai"
Expand Down Expand Up @@ -43,7 +46,9 @@ func InitModule(db *egorm.Component,

ai.InitCommonHandlers,
InitRootHandler,

service.NewGeneralService,
service.NewJDService,
web.NewHandler,
wire.Struct(new(ai.Module), "*"),
wire.FieldsOf(new(*credit.Module), "Svc"),
)
Expand Down
12 changes: 9 additions & 3 deletions internal/ai/internal/integration/startup/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c2d75ee

Please sign in to comment.