• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hicommonwealth / commonwealth / 13013869712

28 Jan 2025 03:30PM UTC coverage: 47.201% (-0.4%) from 47.645%
13013869712

Pull #10687

github

web-flow
Merge 2397c04f9 into 5c8e35b43
Pull Request #10687: Referrals (leaderboard and individual board)

1369 of 3259 branches covered (42.01%)

Branch coverage included in aggregate %.

2695 of 5351 relevant lines covered (50.36%)

72.57 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

9.76
/libs/model/src/services/openai/parseBotCommand.ts
1
import { OpenAI } from 'openai';
2
import {
3
  ChatCompletionMessage,
4
  ChatCompletionTool,
5
} from 'openai/resources/index.mjs';
6
import { config } from '../../config';
7

8
export type ContestMetadataResponse = {
9
  contestName: string;
10
  payoutStructure: number[];
11
  voterShare: number;
12
  image_url: string;
13
  tokenAddress: string;
14
};
15

16
const system_prompt: ChatCompletionMessage = {
58✔
17
  role: 'assistant',
18
  content: `
19
    You are a data extraction system to understand intents of the following style of message: \n
20
    "hey @contestbot create a contest with the token 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913,
21
    with 20% of prize amount allocated to voters and the rest going to two winners.
22
    Winner #1 should have 75% while winner #2 has 25%.
23
    The contest title is “Submit your best artwork for our token”.
24
    Use the following image https://test.com/test.png" \n
25
    This message should result in the following parameters: \n
26
    {
27
      "contestName": "Submit your best artwork for our token",
28
      "payoutStructure": [75, 25],
29
      "voterShare": 20,
30
      "image_url": "https://test.com/test.png",
31
      "tokenAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
32
    }
33
    \n
34
    The payoutStructure refers to the percentage given to each winner and its values must always add up to 100.
35
    Any mention of dividing equally refers to the payoutStructure.
36
    Any mention of shares, allocation or distribution to voters should be assigned to the voterShare
37
    which is independent of payoutStructure.
38
    `,
39
};
40

41
const tools: ChatCompletionTool[] = [
58✔
42
  {
43
    type: 'function',
44
    function: {
45
      name: 'parse_data',
46
      parameters: {
47
        type: 'object',
48
        properties: {
49
          contestName: { type: 'string' },
50
          payoutStructure: { type: 'array', items: { type: 'number' } },
51
          voterShare: { type: 'number' },
52
          image_url: { type: 'string' },
53
          tokenAddress: { type: 'string' },
54
        },
55
      },
56
    },
57
  },
58
];
59

60
// Custom error type that returns a human-readable error intended for end users
61
export class ParseBotCommandError extends Error {
62
  static ERRORS = {
58✔
63
    NoResponse: 'Failed to create contest. Verify your prompt or try again.',
64
    InvalidParams:
65
      'Failed to create contest. Specify all contest parameters: winners, prize distribution to voters, title, image and token address.',
66
  } as const;
67

68
  constructor(message: keyof typeof ParseBotCommandError.ERRORS) {
69
    super(message);
×
70
    this.name = this.constructor.name;
×
71

72
    if (Error.captureStackTrace) {
×
73
      Error.captureStackTrace(this, this.constructor);
×
74
    }
75
  }
76

77
  getPrettyError(): string {
78
    return (
×
79
      ParseBotCommandError.ERRORS[
×
80
        this.message as keyof typeof ParseBotCommandError.ERRORS
81
      ] || 'An unknown error occurred.'
82
    );
83
  }
84
}
85

86
export const parseBotCommand = async (
58✔
87
  command: string,
88
): Promise<ContestMetadataResponse> => {
89
  const openai = new OpenAI({
×
90
    organization: config.OPENAI.ORGANIZATION,
91
    apiKey: config.OPENAI.API_KEY,
92
  });
93

94
  const response = await openai.chat.completions.create({
×
95
    model: 'gpt-4-turbo',
96
    messages: [
97
      system_prompt,
98
      {
99
        role: 'user',
100
        content: command,
101
      },
102
    ],
103
    tools,
104
  });
105

106
  let data = null;
×
107
  try {
×
108
    data = JSON.parse(
×
109
      response.choices[0].message.tool_calls![0].function.arguments,
110
    );
111
  } catch (err) {
112
    throw new ParseBotCommandError('NoResponse');
×
113
  }
114

115
  // if payout structure has any remainder under 100, give it to first winner
116
  if (!data.payoutStructure.length) {
×
117
    throw new ParseBotCommandError('InvalidParams');
×
118
  }
119
  const payoutStructure: Array<number> = data.payoutStructure.map((n: number) =>
×
120
    Math.floor(n),
×
121
  );
122
  const sum = payoutStructure.reduce((p, acc) => acc + p, 0);
×
123
  if (sum < 100) {
×
124
    const remainder = 100 - sum;
×
125
    payoutStructure[0] += remainder;
×
126
  }
127

128
  if (!data.contestName || !data.image_url || !data.tokenAddress) {
×
129
    throw new ParseBotCommandError('InvalidParams');
×
130
  }
131

132
  return {
×
133
    contestName: data.contestName,
134
    payoutStructure,
135
    voterShare: data.voterShare || 0,
×
136
    image_url: data.image_url,
137
    tokenAddress: data.tokenAddress,
138
  };
139
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc